From 42b2832fcbe5452906240951ce4697965ba2b44a Mon Sep 17 00:00:00 2001 From: Harsh Kapse Date: Thu, 13 Aug 2026 14:23:47 +0530 Subject: [PATCH] feat(treesitter): add ruby rules and testdata --- internal/treesitter/parser_test.go | 12 + internal/treesitter/ruby/rules.yml | 157 + internal/treesitter/rules_test.go | 10 + .../expected.json | 6917 ++++++ .../metadata.json | 12 + .../new.rb | 1720 ++ .../old.rb | 1702 ++ .../expected.json | 238 + .../metadata.json | 12 + .../new.rb | 50 + .../old.rb | 50 + .../expected.json | 2776 +++ .../metadata.json | 12 + .../new.rb | 654 + .../old.rb | 654 + .../expected.json | 1087 + .../metadata.json | 12 + .../ruby_sinatra_fix_ci_failures_15/new.rb | 177 + .../ruby_sinatra_fix_ci_failures_15/old.rb | 173 + .../expected.json | 9242 +++++++ .../metadata.json | 12 + .../new.rb | 2213 ++ .../old.rb | 2207 ++ .../expected.json | 991 + .../metadata.json | 12 + .../new.rb | 214 + .../old.rb | 212 + .../expected.json | 317 + .../metadata.json | 12 + .../new.rb | 53 + .../old.rb | 51 + .../expected.json | 1179 + .../metadata.json | 12 + .../new.rb | 116 + .../old.rb | 103 + .../expected.json | 10440 ++++++++ .../metadata.json | 12 + .../new.rb | 2021 ++ .../old.rb | 2012 ++ .../expected.json | 1065 + .../metadata.json | 12 + .../new.rb | 66 + .../old.rb | 66 + .../ruby_sinatra_fix_redos_12/expected.json | 865 + .../ruby_sinatra_fix_redos_12/metadata.json | 12 + .../testdata/ruby_sinatra_fix_redos_12/new.rb | 25 + .../testdata/ruby_sinatra_fix_redos_12/old.rb | 23 + .../ruby_sinatra_fix_redos_13/expected.json | 384 + .../ruby_sinatra_fix_redos_13/metadata.json | 12 + .../testdata/ruby_sinatra_fix_redos_13/new.rb | 27 + .../testdata/ruby_sinatra_fix_redos_13/old.rb | 27 + .../expected.json | 8932 +++++++ .../metadata.json | 12 + .../new.rb | 2170 ++ .../old.rb | 2170 ++ .../expected.json | 1354 ++ .../metadata.json | 12 + .../new.rb | 299 + .../old.rb | 299 + .../expected.json | 20276 ++++++++++++++++ .../metadata.json | 12 + .../new.rb | 2115 ++ .../old.rb | 1 + 63 files changed, 88092 insertions(+) create mode 100644 internal/treesitter/ruby/rules.yml create mode 100644 tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/expected.json create mode 100644 tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/metadata.json create mode 100644 tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/new.rb create mode 100644 tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_ci_failures_15/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_ci_failures_15/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_ci_failures_15/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_ci_failures_15/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_redos_12/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_redos_12/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_redos_12/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_redos_12/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_redos_13/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_redos_13/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_redos_13/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_redos_13/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/old.rb create mode 100644 tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/expected.json create mode 100644 tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/metadata.json create mode 100644 tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/new.rb create mode 100644 tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/old.rb create mode 100644 tests/testdata/ruby_sinatra_use_exception_detailed_message_10/expected.json create mode 100644 tests/testdata/ruby_sinatra_use_exception_detailed_message_10/metadata.json create mode 100644 tests/testdata/ruby_sinatra_use_exception_detailed_message_10/new.rb create mode 100644 tests/testdata/ruby_sinatra_use_exception_detailed_message_10/old.rb diff --git a/internal/treesitter/parser_test.go b/internal/treesitter/parser_test.go index dc4d186..9843df3 100644 --- a/internal/treesitter/parser_test.go +++ b/internal/treesitter/parser_test.go @@ -26,6 +26,7 @@ func TestDetectLanguage(t *testing.T) { {"test.cs", false}, {"test.java", false}, {"test.php", false}, + {"test.rb", false}, {"test.json", false}, {"test.yaml", false}, {"test.yml", false}, @@ -195,6 +196,17 @@ int main() { std::cout << "Hello C++\n"; return 0; }`) t.Error("Parse(): expected non-nil AST root") } }) + + t.Run("valid ruby source", func(t *testing.T) { + src := []byte(`def hello; puts "Hello World"; end`) + ast, err := Parse(src, "main.rb") + if err != nil { + t.Fatalf("Parse(): unexpected error: %v", err) + } + if ast == nil { + t.Error("Parse(): expected non-nil AST root") + } + }) t.Run("unknown extension", func(t *testing.T) { src := []byte(`some text`) ast, err := Parse(src, "main.xyz") diff --git a/internal/treesitter/ruby/rules.yml b/internal/treesitter/ruby/rules.yml new file mode 100644 index 0000000..fb6bb6d --- /dev/null +++ b/internal/treesitter/ruby/rules.yml @@ -0,0 +1,157 @@ +flattened: + - string + - string_content + - heredoc_body + - comment + - integer + - float + - simple_symbol + - symbol + - identifier + - instance_variable + - class_variable + - global_variable + - constant + +aliased: + # Comparison operators + "==": comparison_operator_literal + "!=": comparison_operator_literal + "===": comparison_operator_literal + "<=>": comparison_operator_literal + "<": comparison_operator_literal + "<=": comparison_operator_literal + ">": comparison_operator_literal + ">=": comparison_operator_literal + "=~": comparison_operator_literal + "!~": comparison_operator_literal + + # Logical operators + "&&": logical_operator_literal + "||": logical_operator_literal + "!": unary_operator_literal + + # Assignment operators + "=": assignment_operator_literal + "+=": assignment_operator_literal + "-=": assignment_operator_literal + "*=": assignment_operator_literal + "/=": assignment_operator_literal + "%=": assignment_operator_literal + "**=": assignment_operator_literal + "&=": assignment_operator_literal + "|=": assignment_operator_literal + "^=": assignment_operator_literal + "<<=": assignment_operator_literal + ">>=": assignment_operator_literal + "||=": assignment_operator_literal + "&&=": assignment_operator_literal + + # Arithmetic operators + "+": arithmetic_operator_literal + "-": arithmetic_operator_literal + "*": arithmetic_operator_literal + "/": arithmetic_operator_literal + "%": arithmetic_operator_literal + "**": arithmetic_operator_literal + + # Bitwise operators + "&": bitwise_operator_literal + "|": bitwise_operator_literal + "^": bitwise_operator_literal + "<<": bitwise_operator_literal + ">>": bitwise_operator_literal + "~": bitwise_operator_literal + +ignored: + - "\n" + - "(" + - ")" + - "{" + - "}" + - "[" + - "]" + - "," + - ";" + - ":" + - "::" + - "." + - "->" + - "|" + - "=>" + +scaffolding: + - program + - body_statement + - method + - singleton_method + - class + - module + - if + - unless + - elsif + - else + - while + - until + - for + - case + - when + - begin + - rescue + - ensure + - call + - method_call + - argument_list + - method_parameters + - block_parameters + - do_block + - block + - assignment + - operator_assignment + - hash + - array + - pair + +keywords: + - def + - class + - module + - if + - unless + - elsif + - else + - case + - when + - while + - until + - for + - do + - begin + - rescue + - ensure + - end + - return + - yield + - break + - next + - redo + - retry + - alias + - undef + - and + - or + - not + - then + - in + - self + - nil + - true + - false + - super + +declarations: + - method + - singleton_method + - class + - module + diff --git a/internal/treesitter/rules_test.go b/internal/treesitter/rules_test.go index bce1aef..e8cbbc1 100644 --- a/internal/treesitter/rules_test.go +++ b/internal/treesitter/rules_test.go @@ -136,6 +136,16 @@ func TestRulesAliased(t *testing.T) { "++", "--", "??", }, }, + { + lang: "ruby", + operators: []string{ + "==", "!=", "===", "<=>", "<", "<=", ">", ">=", "=~", "!~", + "&&", "||", "!", + "=", "+=", "-=", "*=", "/=", "%=", "**=", "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=", + "+", "-", "*", "/", "%", "**", + "&", "|", "^", "<<", ">>", "~", + }, + }, } for _, tt := range tests { diff --git a/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/expected.json b/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/expected.json new file mode 100644 index 0000000..f8b7bd6 --- /dev/null +++ b/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/expected.json @@ -0,0 +1,6917 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 4, + 3, + 66 + ], + "type": "call", + "start_byte": 20069, + "end_byte": 20722 + }, + "parent": { + "tree": "after", + "path": [ + 4, + 3 + ], + "type": "body_statement", + "start_byte": 352, + "end_byte": 40480 + }, + "position": 66, + "subtree": true + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": 277, + "right_line": 277 + }, + { + "left_line": 278, + "right_line": 278 + }, + { + "left_line": 279, + "right_line": 279 + }, + { + "left_line": 280, + "right_line": 280 + }, + { + "left_line": 281, + "right_line": 281 + }, + { + "left_line": 282, + "right_line": 282 + }, + { + "left_line": 283, + "right_line": 283 + }, + { + "left_line": 284, + "right_line": 284 + }, + { + "left_line": 285, + "right_line": 285 + }, + { + "left_line": 286, + "right_line": 286 + }, + { + "left_line": 287, + "right_line": 287 + }, + { + "left_line": 288, + "right_line": 288 + }, + { + "left_line": 289, + "right_line": 289 + }, + { + "left_line": 290, + "right_line": 290 + }, + { + "left_line": 291, + "right_line": 291 + }, + { + "left_line": 292, + "right_line": 292 + }, + { + "left_line": 293, + "right_line": 293 + }, + { + "left_line": 294, + "right_line": 294 + }, + { + "left_line": 295, + "right_line": 295 + }, + { + "left_line": 296, + "right_line": 296 + }, + { + "left_line": 297, + "right_line": 297 + }, + { + "left_line": 298, + "right_line": 298 + }, + { + "left_line": 299, + "right_line": 299 + }, + { + "left_line": 300, + "right_line": 300 + }, + { + "left_line": 301, + "right_line": 301 + }, + { + "left_line": 302, + "right_line": 302 + }, + { + "left_line": 303, + "right_line": 303 + }, + { + "left_line": 304, + "right_line": 304 + }, + { + "left_line": 305, + "right_line": 305 + }, + { + "left_line": 306, + "right_line": 306 + }, + { + "left_line": 307, + "right_line": 307 + }, + { + "left_line": 308, + "right_line": 308 + }, + { + "left_line": 309, + "right_line": 309 + }, + { + "left_line": 310, + "right_line": 310 + }, + { + "left_line": 311, + "right_line": 311 + }, + { + "left_line": 312, + "right_line": 312 + }, + { + "left_line": 313, + "right_line": 313 + }, + { + "left_line": 314, + "right_line": 314 + }, + { + "left_line": 315, + "right_line": 315 + }, + { + "left_line": 316, + "right_line": 316 + }, + { + "left_line": 317, + "right_line": 317 + }, + { + "left_line": 318, + "right_line": 318 + }, + { + "left_line": 319, + "right_line": 319 + }, + { + "left_line": 320, + "right_line": 320 + }, + { + "left_line": 321, + "right_line": 321 + }, + { + "left_line": 322, + "right_line": 322 + }, + { + "left_line": 323, + "right_line": 323 + }, + { + "left_line": 324, + "right_line": 324 + }, + { + "left_line": 325, + "right_line": 325 + }, + { + "left_line": 326, + "right_line": 326 + }, + { + "left_line": 327, + "right_line": 327 + }, + { + "left_line": 328, + "right_line": 328 + }, + { + "left_line": 329, + "right_line": 329 + }, + { + "left_line": 330, + "right_line": 330 + }, + { + "left_line": 331, + "right_line": 331 + }, + { + "left_line": 332, + "right_line": 332 + }, + { + "left_line": 333, + "right_line": 333 + }, + { + "left_line": 334, + "right_line": 334 + }, + { + "left_line": 335, + "right_line": 335 + }, + { + "left_line": 336, + "right_line": 336 + }, + { + "left_line": 337, + "right_line": 337 + }, + { + "left_line": 338, + "right_line": 338 + }, + { + "left_line": 339, + "right_line": 339 + }, + { + "left_line": 340, + "right_line": 340 + }, + { + "left_line": 341, + "right_line": 341 + }, + { + "left_line": 342, + "right_line": 342 + }, + { + "left_line": 343, + "right_line": 343 + }, + { + "left_line": 344, + "right_line": 344 + }, + { + "left_line": 345, + "right_line": 345 + }, + { + "left_line": 346, + "right_line": 346 + }, + { + "left_line": 347, + "right_line": 347 + }, + { + "left_line": 348, + "right_line": 348 + }, + { + "left_line": 349, + "right_line": 349 + }, + { + "left_line": 350, + "right_line": 350 + }, + { + "left_line": 351, + "right_line": 351 + }, + { + "left_line": 352, + "right_line": 352 + }, + { + "left_line": 353, + "right_line": 353 + }, + { + "left_line": 354, + "right_line": 354 + }, + { + "left_line": 355, + "right_line": 355 + }, + { + "left_line": 356, + "right_line": 356 + }, + { + "left_line": 357, + "right_line": 357 + }, + { + "left_line": 358, + "right_line": 358 + }, + { + "left_line": 359, + "right_line": 359 + }, + { + "left_line": 360, + "right_line": 360 + }, + { + "left_line": 361, + "right_line": 361 + }, + { + "left_line": 362, + "right_line": 362 + }, + { + "left_line": 363, + "right_line": 363 + }, + { + "left_line": 364, + "right_line": 364 + }, + { + "left_line": 365, + "right_line": 365 + }, + { + "left_line": 366, + "right_line": 366 + }, + { + "left_line": 367, + "right_line": 367 + }, + { + "left_line": 368, + "right_line": 368 + }, + { + "left_line": 369, + "right_line": 369 + }, + { + "left_line": 370, + "right_line": 370 + }, + { + "left_line": 371, + "right_line": 371 + }, + { + "left_line": 372, + "right_line": 372 + }, + { + "left_line": 373, + "right_line": 373 + }, + { + "left_line": 374, + "right_line": 374 + }, + { + "left_line": 375, + "right_line": 375 + }, + { + "left_line": 376, + "right_line": 376 + }, + { + "left_line": 377, + "right_line": 377 + }, + { + "left_line": 378, + "right_line": 378 + }, + { + "left_line": 379, + "right_line": 379 + }, + { + "left_line": 380, + "right_line": 380 + }, + { + "left_line": 381, + "right_line": 381 + }, + { + "left_line": 382, + "right_line": 382 + }, + { + "left_line": 383, + "right_line": 383 + }, + { + "left_line": 384, + "right_line": 384 + }, + { + "left_line": 385, + "right_line": 385 + }, + { + "left_line": 386, + "right_line": 386 + }, + { + "left_line": 387, + "right_line": 387 + }, + { + "left_line": 388, + "right_line": 388 + }, + { + "left_line": 389, + "right_line": 389 + }, + { + "left_line": 390, + "right_line": 390 + }, + { + "left_line": 391, + "right_line": 391 + }, + { + "left_line": 392, + "right_line": 392 + }, + { + "left_line": 393, + "right_line": 393 + }, + { + "left_line": 394, + "right_line": 394 + }, + { + "left_line": 395, + "right_line": 395 + }, + { + "left_line": 396, + "right_line": 396 + }, + { + "left_line": 397, + "right_line": 397 + }, + { + "left_line": 398, + "right_line": 398 + }, + { + "left_line": 399, + "right_line": 399 + }, + { + "left_line": 400, + "right_line": 400 + }, + { + "left_line": 401, + "right_line": 401 + }, + { + "left_line": 402, + "right_line": 402 + }, + { + "left_line": 403, + "right_line": 403 + }, + { + "left_line": 404, + "right_line": 404 + }, + { + "left_line": 405, + "right_line": 405 + }, + { + "left_line": 406, + "right_line": 406 + }, + { + "left_line": 407, + "right_line": 407 + }, + { + "left_line": 408, + "right_line": 408 + }, + { + "left_line": 409, + "right_line": 409 + }, + { + "left_line": 410, + "right_line": 410 + }, + { + "left_line": 411, + "right_line": 411 + }, + { + "left_line": 412, + "right_line": 412 + }, + { + "left_line": 413, + "right_line": 413 + }, + { + "left_line": 414, + "right_line": 414 + }, + { + "left_line": 415, + "right_line": 415 + }, + { + "left_line": 416, + "right_line": 416 + }, + { + "left_line": 417, + "right_line": 417 + }, + { + "left_line": 418, + "right_line": 418 + }, + { + "left_line": 419, + "right_line": 419 + }, + { + "left_line": 420, + "right_line": 420 + }, + { + "left_line": 421, + "right_line": 421 + }, + { + "left_line": 422, + "right_line": 422 + }, + { + "left_line": 423, + "right_line": 423 + }, + { + "left_line": 424, + "right_line": 424 + }, + { + "left_line": 425, + "right_line": 425 + }, + { + "left_line": 426, + "right_line": 426 + }, + { + "left_line": 427, + "right_line": 427 + }, + { + "left_line": 428, + "right_line": 428 + }, + { + "left_line": 429, + "right_line": 429 + }, + { + "left_line": 430, + "right_line": 430 + }, + { + "left_line": 431, + "right_line": 431 + }, + { + "left_line": 432, + "right_line": 432 + }, + { + "left_line": 433, + "right_line": 433 + }, + { + "left_line": 434, + "right_line": 434 + }, + { + "left_line": 435, + "right_line": 435 + }, + { + "left_line": 436, + "right_line": 436 + }, + { + "left_line": 437, + "right_line": 437 + }, + { + "left_line": 438, + "right_line": 438 + }, + { + "left_line": 439, + "right_line": 439 + }, + { + "left_line": 440, + "right_line": 440 + }, + { + "left_line": 441, + "right_line": 441 + }, + { + "left_line": 442, + "right_line": 442 + }, + { + "left_line": 443, + "right_line": 443 + }, + { + "left_line": 444, + "right_line": 444 + }, + { + "left_line": 445, + "right_line": 445 + }, + { + "left_line": 446, + "right_line": 446 + }, + { + "left_line": 447, + "right_line": 447 + }, + { + "left_line": 448, + "right_line": 448 + }, + { + "left_line": 449, + "right_line": 449 + }, + { + "left_line": 450, + "right_line": 450 + }, + { + "left_line": 451, + "right_line": 451 + }, + { + "left_line": 452, + "right_line": 452 + }, + { + "left_line": 453, + "right_line": 453 + }, + { + "left_line": 454, + "right_line": 454 + }, + { + "left_line": 455, + "right_line": 455 + }, + { + "left_line": 456, + "right_line": 456 + }, + { + "left_line": 457, + "right_line": 457 + }, + { + "left_line": 458, + "right_line": 458 + }, + { + "left_line": 459, + "right_line": 459 + }, + { + "left_line": 460, + "right_line": 460 + }, + { + "left_line": 461, + "right_line": 461 + }, + { + "left_line": 462, + "right_line": 462 + }, + { + "left_line": 463, + "right_line": 463 + }, + { + "left_line": 464, + "right_line": 464 + }, + { + "left_line": 465, + "right_line": 465 + }, + { + "left_line": 466, + "right_line": 466 + }, + { + "left_line": 467, + "right_line": 467 + }, + { + "left_line": 468, + "right_line": 468 + }, + { + "left_line": 469, + "right_line": 469 + }, + { + "left_line": 470, + "right_line": 470 + }, + { + "left_line": 471, + "right_line": 471 + }, + { + "left_line": 472, + "right_line": 472 + }, + { + "left_line": 473, + "right_line": 473 + }, + { + "left_line": 474, + "right_line": 474 + }, + { + "left_line": 475, + "right_line": 475 + }, + { + "left_line": 476, + "right_line": 476 + }, + { + "left_line": 477, + "right_line": 477 + }, + { + "left_line": 478, + "right_line": 478 + }, + { + "left_line": 479, + "right_line": 479 + }, + { + "left_line": 480, + "right_line": 480 + }, + { + "left_line": 481, + "right_line": 481 + }, + { + "left_line": 482, + "right_line": 482 + }, + { + "left_line": 483, + "right_line": 483 + }, + { + "left_line": 484, + "right_line": 484 + }, + { + "left_line": 485, + "right_line": 485 + }, + { + "left_line": 486, + "right_line": 486 + }, + { + "left_line": 487, + "right_line": 487 + }, + { + "left_line": 488, + "right_line": 488 + }, + { + "left_line": 489, + "right_line": 489 + }, + { + "left_line": 490, + "right_line": 490 + }, + { + "left_line": 491, + "right_line": 491 + }, + { + "left_line": 492, + "right_line": 492 + }, + { + "left_line": 493, + "right_line": 493 + }, + { + "left_line": 494, + "right_line": 494 + }, + { + "left_line": 495, + "right_line": 495 + }, + { + "left_line": 496, + "right_line": 496 + }, + { + "left_line": 497, + "right_line": 497 + }, + { + "left_line": 498, + "right_line": 498 + }, + { + "left_line": 499, + "right_line": 499 + }, + { + "left_line": 500, + "right_line": 500 + }, + { + "left_line": 501, + "right_line": 501 + }, + { + "left_line": 502, + "right_line": 502 + }, + { + "left_line": 503, + "right_line": 503 + }, + { + "left_line": 504, + "right_line": 504 + }, + { + "left_line": 505, + "right_line": 505 + }, + { + "left_line": 506, + "right_line": 506 + }, + { + "left_line": 507, + "right_line": 507 + }, + { + "left_line": 508, + "right_line": 508 + }, + { + "left_line": 509, + "right_line": 509 + }, + { + "left_line": 510, + "right_line": 510 + }, + { + "left_line": 511, + "right_line": 511 + }, + { + "left_line": 512, + "right_line": 512 + }, + { + "left_line": 513, + "right_line": 513 + }, + { + "left_line": 514, + "right_line": 514 + }, + { + "left_line": 515, + "right_line": 515 + }, + { + "left_line": 516, + "right_line": 516 + }, + { + "left_line": 517, + "right_line": 517 + }, + { + "left_line": 518, + "right_line": 518 + }, + { + "left_line": 519, + "right_line": 519 + }, + { + "left_line": 520, + "right_line": 520 + }, + { + "left_line": 521, + "right_line": 521 + }, + { + "left_line": 522, + "right_line": 522 + }, + { + "left_line": 523, + "right_line": 523 + }, + { + "left_line": 524, + "right_line": 524 + }, + { + "left_line": 525, + "right_line": 525 + }, + { + "left_line": 526, + "right_line": 526 + }, + { + "left_line": 527, + "right_line": 527 + }, + { + "left_line": 528, + "right_line": 528 + }, + { + "left_line": 529, + "right_line": 529 + }, + { + "left_line": 530, + "right_line": 530 + }, + { + "left_line": 531, + "right_line": 531 + }, + { + "left_line": 532, + "right_line": 532 + }, + { + "left_line": 533, + "right_line": 533 + }, + { + "left_line": 534, + "right_line": 534 + }, + { + "left_line": 535, + "right_line": 535 + }, + { + "left_line": 536, + "right_line": 536 + }, + { + "left_line": 537, + "right_line": 537 + }, + { + "left_line": 538, + "right_line": 538 + }, + { + "left_line": 539, + "right_line": 539 + }, + { + "left_line": 540, + "right_line": 540 + }, + { + "left_line": 541, + "right_line": 541 + }, + { + "left_line": 542, + "right_line": 542 + }, + { + "left_line": 543, + "right_line": 543 + }, + { + "left_line": 544, + "right_line": 544 + }, + { + "left_line": 545, + "right_line": 545 + }, + { + "left_line": 546, + "right_line": 546 + }, + { + "left_line": 547, + "right_line": 547 + }, + { + "left_line": 548, + "right_line": 548 + }, + { + "left_line": 549, + "right_line": 549 + }, + { + "left_line": 550, + "right_line": 550 + }, + { + "left_line": 551, + "right_line": 551 + }, + { + "left_line": 552, + "right_line": 552 + }, + { + "left_line": 553, + "right_line": 553 + }, + { + "left_line": 554, + "right_line": 554 + }, + { + "left_line": 555, + "right_line": 555 + }, + { + "left_line": 556, + "right_line": 556 + }, + { + "left_line": 557, + "right_line": 557 + }, + { + "left_line": 558, + "right_line": 558 + }, + { + "left_line": 559, + "right_line": 559 + }, + { + "left_line": 560, + "right_line": 560 + }, + { + "left_line": 561, + "right_line": 561 + }, + { + "left_line": 562, + "right_line": 562 + }, + { + "left_line": 563, + "right_line": 563 + }, + { + "left_line": 564, + "right_line": 564 + }, + { + "left_line": 565, + "right_line": 565 + }, + { + "left_line": 566, + "right_line": 566 + }, + { + "left_line": 567, + "right_line": 567 + }, + { + "left_line": 568, + "right_line": 568 + }, + { + "left_line": 569, + "right_line": 569 + }, + { + "left_line": 570, + "right_line": 570 + }, + { + "left_line": 571, + "right_line": 571 + }, + { + "left_line": 572, + "right_line": 572 + }, + { + "left_line": 573, + "right_line": 573 + }, + { + "left_line": 574, + "right_line": 574 + }, + { + "left_line": 575, + "right_line": 575 + }, + { + "left_line": 576, + "right_line": 576 + }, + { + "left_line": 577, + "right_line": 577 + }, + { + "left_line": 578, + "right_line": 578 + }, + { + "left_line": 579, + "right_line": 579 + }, + { + "left_line": 580, + "right_line": 580 + }, + { + "left_line": 581, + "right_line": 581 + }, + { + "left_line": 582, + "right_line": 582 + }, + { + "left_line": 583, + "right_line": 583 + }, + { + "left_line": 584, + "right_line": 584 + }, + { + "left_line": 585, + "right_line": 585 + }, + { + "left_line": 586, + "right_line": 586 + }, + { + "left_line": 587, + "right_line": 587 + }, + { + "left_line": 588, + "right_line": 588 + }, + { + "left_line": 589, + "right_line": 589 + }, + { + "left_line": 590, + "right_line": 590 + }, + { + "left_line": 591, + "right_line": 591 + }, + { + "left_line": 592, + "right_line": 592 + }, + { + "left_line": 593, + "right_line": 593 + }, + { + "left_line": 594, + "right_line": 594 + }, + { + "left_line": 595, + "right_line": 595 + }, + { + "left_line": 596, + "right_line": 596 + }, + { + "left_line": 597, + "right_line": 597 + }, + { + "left_line": 598, + "right_line": 598 + }, + { + "left_line": 599, + "right_line": 599 + }, + { + "left_line": 600, + "right_line": 600 + }, + { + "left_line": 601, + "right_line": 601 + }, + { + "left_line": 602, + "right_line": 602 + }, + { + "left_line": 603, + "right_line": 603 + }, + { + "left_line": 604, + "right_line": 604 + }, + { + "left_line": 605, + "right_line": 605 + }, + { + "left_line": 606, + "right_line": 606 + }, + { + "left_line": 607, + "right_line": 607 + }, + { + "left_line": 608, + "right_line": 608 + }, + { + "left_line": 609, + "right_line": 609 + }, + { + "left_line": 610, + "right_line": 610 + }, + { + "left_line": 611, + "right_line": 611 + }, + { + "left_line": 612, + "right_line": 612 + }, + { + "left_line": 613, + "right_line": 613 + }, + { + "left_line": 614, + "right_line": 614 + }, + { + "left_line": 615, + "right_line": 615 + }, + { + "left_line": 616, + "right_line": 616 + }, + { + "left_line": 617, + "right_line": 617 + }, + { + "left_line": 618, + "right_line": 618 + }, + { + "left_line": 619, + "right_line": 619 + }, + { + "left_line": 620, + "right_line": 620 + }, + { + "left_line": 621, + "right_line": 621 + }, + { + "left_line": 622, + "right_line": 622 + }, + { + "left_line": 623, + "right_line": 623 + }, + { + "left_line": 624, + "right_line": 624 + }, + { + "left_line": 625, + "right_line": 625 + }, + { + "left_line": 626, + "right_line": 626 + }, + { + "left_line": 627, + "right_line": 627 + }, + { + "left_line": 628, + "right_line": 628 + }, + { + "left_line": 629, + "right_line": 629 + }, + { + "left_line": 630, + "right_line": 630 + }, + { + "left_line": 631, + "right_line": 631 + }, + { + "left_line": 632, + "right_line": 632 + }, + { + "left_line": 633, + "right_line": 633 + }, + { + "left_line": 634, + "right_line": 634 + }, + { + "left_line": 635, + "right_line": 635 + }, + { + "left_line": 636, + "right_line": 636 + }, + { + "left_line": 637, + "right_line": 637 + }, + { + "left_line": 638, + "right_line": 638 + }, + { + "left_line": 639, + "right_line": 639 + }, + { + "left_line": 640, + "right_line": 640 + }, + { + "left_line": 641, + "right_line": 641 + }, + { + "left_line": 642, + "right_line": 642 + }, + { + "left_line": 643, + "right_line": 643 + }, + { + "left_line": 644, + "right_line": 644 + }, + { + "left_line": 645, + "right_line": 645 + }, + { + "left_line": 646, + "right_line": 646 + }, + { + "left_line": 647, + "right_line": 647 + }, + { + "left_line": 648, + "right_line": 648 + }, + { + "left_line": 649, + "right_line": 649 + }, + { + "left_line": 650, + "right_line": 650 + }, + { + "left_line": 651, + "right_line": 651 + }, + { + "left_line": 652, + "right_line": 652 + }, + { + "left_line": 653, + "right_line": 653 + }, + { + "left_line": 654, + "right_line": 654 + }, + { + "left_line": 655, + "right_line": 655 + }, + { + "left_line": 656, + "right_line": 656 + }, + { + "left_line": 657, + "right_line": 657 + }, + { + "left_line": 658, + "right_line": 658 + }, + { + "left_line": 659, + "right_line": 659 + }, + { + "left_line": 660, + "right_line": 660 + }, + { + "left_line": 661, + "right_line": 661 + }, + { + "left_line": 662, + "right_line": 662 + }, + { + "left_line": 663, + "right_line": 663 + }, + { + "left_line": 664, + "right_line": 664 + }, + { + "left_line": 665, + "right_line": 665 + }, + { + "left_line": 666, + "right_line": 666 + }, + { + "left_line": 667, + "right_line": 667 + }, + { + "left_line": 668, + "right_line": 668 + }, + { + "left_line": 669, + "right_line": 669 + }, + { + "left_line": 670, + "right_line": 670 + }, + { + "left_line": 671, + "right_line": 671 + }, + { + "left_line": 672, + "right_line": 672 + }, + { + "left_line": 673, + "right_line": 673 + }, + { + "left_line": 674, + "right_line": 674 + }, + { + "left_line": 675, + "right_line": 675 + }, + { + "left_line": 676, + "right_line": 676 + }, + { + "left_line": 677, + "right_line": 677 + }, + { + "left_line": 678, + "right_line": 678 + }, + { + "left_line": 679, + "right_line": 679 + }, + { + "left_line": 680, + "right_line": 680 + }, + { + "left_line": 681, + "right_line": 681 + }, + { + "left_line": 682, + "right_line": 682 + }, + { + "left_line": 683, + "right_line": 683 + }, + { + "left_line": 684, + "right_line": 684 + }, + { + "left_line": 685, + "right_line": 685 + }, + { + "left_line": 686, + "right_line": 686 + }, + { + "left_line": 687, + "right_line": 687 + }, + { + "left_line": 688, + "right_line": 688 + }, + { + "left_line": 689, + "right_line": 689 + }, + { + "left_line": 690, + "right_line": 690 + }, + { + "left_line": 691, + "right_line": 691 + }, + { + "left_line": 692, + "right_line": 692 + }, + { + "left_line": 693, + "right_line": 693 + }, + { + "left_line": 694, + "right_line": 694 + }, + { + "left_line": 695, + "right_line": 695 + }, + { + "left_line": 696, + "right_line": 696 + }, + { + "left_line": 697, + "right_line": 697 + }, + { + "left_line": 698, + "right_line": 698 + }, + { + "left_line": 699, + "right_line": 699 + }, + { + "left_line": 700, + "right_line": 700 + }, + { + "left_line": 701, + "right_line": 701 + }, + { + "left_line": 702, + "right_line": 702 + }, + { + "left_line": 703, + "right_line": 703 + }, + { + "left_line": 704, + "right_line": 704 + }, + { + "left_line": 705, + "right_line": 705 + }, + { + "left_line": 706, + "right_line": 706 + }, + { + "left_line": 707, + "right_line": 707 + }, + { + "left_line": 708, + "right_line": 708 + }, + { + "left_line": 709, + "right_line": 709 + }, + { + "left_line": 710, + "right_line": 710 + }, + { + "left_line": 711, + "right_line": 711 + }, + { + "left_line": 712, + "right_line": 712 + }, + { + "left_line": 713, + "right_line": 713 + }, + { + "left_line": 714, + "right_line": 714 + }, + { + "left_line": 715, + "right_line": 715 + }, + { + "left_line": 716, + "right_line": 716 + }, + { + "left_line": 717, + "right_line": 717 + }, + { + "left_line": 718, + "right_line": 718 + }, + { + "left_line": 719, + "right_line": 719 + }, + { + "left_line": 720, + "right_line": 720 + }, + { + "left_line": 721, + "right_line": 721 + }, + { + "left_line": 722, + "right_line": 722 + }, + { + "left_line": 723, + "right_line": 723 + }, + { + "left_line": 724, + "right_line": 724 + }, + { + "left_line": 725, + "right_line": 725 + }, + { + "left_line": 726, + "right_line": 726 + }, + { + "left_line": 727, + "right_line": 727 + }, + { + "left_line": 728, + "right_line": 728 + }, + { + "left_line": 729, + "right_line": 729 + }, + { + "left_line": 730, + "right_line": 730 + }, + { + "left_line": 731, + "right_line": 731 + }, + { + "left_line": 732, + "right_line": 732 + }, + { + "left_line": 733, + "right_line": 733 + }, + { + "left_line": 734, + "right_line": 734 + }, + { + "left_line": 735, + "right_line": 735 + }, + { + "left_line": 736, + "right_line": 736 + }, + { + "left_line": 737, + "right_line": 737 + }, + { + "left_line": 738, + "right_line": 738 + }, + { + "left_line": 739, + "right_line": 739 + }, + { + "left_line": 740, + "right_line": 740 + }, + { + "left_line": 741, + "right_line": 741 + }, + { + "left_line": 742, + "right_line": 742 + }, + { + "left_line": 743, + "right_line": 743 + }, + { + "left_line": 744, + "right_line": 744 + }, + { + "left_line": 745, + "right_line": 745 + }, + { + "left_line": 746, + "right_line": 746 + }, + { + "left_line": 747, + "right_line": 747 + }, + { + "left_line": 748, + "right_line": 748 + }, + { + "left_line": 749, + "right_line": 749 + }, + { + "left_line": 750, + "right_line": 750 + }, + { + "left_line": 751, + "right_line": 751 + }, + { + "left_line": 752, + "right_line": 752 + }, + { + "left_line": 753, + "right_line": 753 + }, + { + "left_line": 754, + "right_line": 754 + }, + { + "left_line": 755, + "right_line": 755 + }, + { + "left_line": 756, + "right_line": 756 + }, + { + "left_line": 757, + "right_line": 757 + }, + { + "left_line": 758, + "right_line": 758 + }, + { + "left_line": 759, + "right_line": 759 + }, + { + "left_line": 760, + "right_line": 760 + }, + { + "left_line": 761, + "right_line": 761 + }, + { + "left_line": 762, + "right_line": 762 + }, + { + "left_line": 763, + "right_line": 763 + }, + { + "left_line": 764, + "right_line": 764 + }, + { + "left_line": 765, + "right_line": 765 + }, + { + "left_line": 766, + "right_line": 766 + }, + { + "left_line": 767, + "right_line": 767 + }, + { + "left_line": 768, + "right_line": 768 + }, + { + "left_line": 769, + "right_line": 769 + }, + { + "left_line": 770, + "right_line": 770 + }, + { + "left_line": 771, + "right_line": 771 + }, + { + "left_line": 772, + "right_line": 772 + }, + { + "left_line": 773, + "right_line": 773 + }, + { + "left_line": 774, + "right_line": 774 + }, + { + "left_line": 775, + "right_line": 775 + }, + { + "left_line": 776, + "right_line": 776 + }, + { + "left_line": 777, + "right_line": 777 + }, + { + "left_line": 778, + "right_line": 778 + }, + { + "left_line": 779, + "right_line": 779 + }, + { + "left_line": 780, + "right_line": 780 + }, + { + "left_line": 781, + "right_line": 781 + }, + { + "left_line": 782, + "right_line": 782 + }, + { + "left_line": 783, + "right_line": 783 + }, + { + "left_line": 784, + "right_line": 784 + }, + { + "left_line": 785, + "right_line": 785 + }, + { + "left_line": 786, + "right_line": 786 + }, + { + "left_line": 787, + "right_line": 787 + }, + { + "left_line": 788, + "right_line": 788 + }, + { + "left_line": 789, + "right_line": 789 + }, + { + "left_line": 790, + "right_line": 790 + }, + { + "left_line": 791, + "right_line": 791 + }, + { + "left_line": 792, + "right_line": 792 + }, + { + "left_line": 793, + "right_line": 793 + }, + { + "left_line": 794, + "right_line": 794 + }, + { + "left_line": 795, + "right_line": 795 + }, + { + "left_line": 796, + "right_line": 796 + }, + { + "left_line": 797, + "right_line": 797 + }, + { + "left_line": 798, + "right_line": 798 + }, + { + "left_line": 799, + "right_line": 799 + }, + { + "left_line": 800, + "right_line": 800 + }, + { + "left_line": 801, + "right_line": 801 + }, + { + "left_line": 802, + "right_line": 802 + }, + { + "left_line": 803, + "right_line": 803 + }, + { + "left_line": 804, + "right_line": 804 + }, + { + "left_line": 805, + "right_line": 805 + }, + { + "left_line": 806, + "right_line": 806 + }, + { + "left_line": 807, + "right_line": 807 + }, + { + "left_line": 808, + "right_line": 808 + }, + { + "left_line": 809, + "right_line": 809 + }, + { + "left_line": 810, + "right_line": 810 + }, + { + "left_line": 811, + "right_line": 811 + }, + { + "left_line": 812, + "right_line": 812 + }, + { + "left_line": 813, + "right_line": 813 + }, + { + "left_line": 814, + "right_line": 814 + }, + { + "left_line": 815, + "right_line": 815 + }, + { + "left_line": 816, + "right_line": 816 + }, + { + "left_line": 817, + "right_line": 817 + }, + { + "left_line": 818, + "right_line": 818 + }, + { + "left_line": 819, + "right_line": 819 + }, + { + "left_line": 820, + "right_line": 820 + }, + { + "left_line": 821, + "right_line": 821 + }, + { + "left_line": 822, + "right_line": 822 + }, + { + "left_line": 823, + "right_line": 823 + }, + { + "left_line": 824, + "right_line": 824 + }, + { + "left_line": 825, + "right_line": 825 + }, + { + "left_line": 826, + "right_line": 826 + }, + { + "left_line": 827, + "right_line": 827 + }, + { + "left_line": 828, + "right_line": 828 + }, + { + "left_line": 829, + "right_line": 829 + }, + { + "left_line": 830, + "right_line": 830 + }, + { + "left_line": 831, + "right_line": 831 + }, + { + "left_line": 832, + "right_line": 832 + }, + { + "left_line": 833, + "right_line": 833 + }, + { + "left_line": 834, + "right_line": 834 + }, + { + "left_line": 835, + "right_line": 835 + }, + { + "left_line": 836, + "right_line": 836 + }, + { + "left_line": 837, + "right_line": 837 + }, + { + "left_line": 838, + "right_line": 838 + }, + { + "left_line": 839, + "right_line": 839 + }, + { + "left_line": 840, + "right_line": 840 + }, + { + "left_line": 841, + "right_line": 841 + }, + { + "left_line": 842, + "right_line": 842 + }, + { + "left_line": 843, + "right_line": 843 + }, + { + "left_line": 844, + "right_line": 844 + }, + { + "left_line": 845, + "right_line": 845 + }, + { + "left_line": 846, + "right_line": 846 + }, + { + "left_line": 847, + "right_line": 847 + }, + { + "left_line": 848, + "right_line": 848 + }, + { + "left_line": 849, + "right_line": 849 + }, + { + "left_line": 850, + "right_line": 850 + }, + { + "left_line": 851, + "right_line": 851 + }, + { + "left_line": 852, + "right_line": 852 + }, + { + "left_line": 853, + "right_line": 853 + }, + { + "left_line": 854, + "right_line": 854 + }, + { + "left_line": 855, + "right_line": 855 + }, + { + "left_line": 856, + "right_line": 856 + }, + { + "left_line": 857, + "right_line": 857 + }, + { + "left_line": 858, + "right_line": 858 + }, + { + "left_line": 859, + "right_line": 859 + }, + { + "left_line": 860, + "right_line": 860 + }, + { + "left_line": 861, + "right_line": 861 + }, + { + "left_line": 862, + "right_line": 862 + }, + { + "left_line": 863, + "right_line": 863 + }, + { + "left_line": 864, + "right_line": 864 + }, + { + "left_line": 865, + "right_line": 865 + }, + { + "left_line": 866, + "right_line": 866 + }, + { + "left_line": 867, + "right_line": 867 + }, + { + "left_line": 868, + "right_line": 868 + }, + { + "left_line": 869, + "right_line": 869 + }, + { + "left_line": 870, + "right_line": 870 + }, + { + "left_line": 871, + "right_line": 871 + }, + { + "left_line": 872, + "right_line": 872 + }, + { + "left_line": 873, + "right_line": 873 + }, + { + "left_line": 874, + "right_line": 874 + }, + { + "left_line": 875, + "right_line": 875 + }, + { + "left_line": 876, + "right_line": 876 + }, + { + "left_line": 877, + "right_line": 877 + }, + { + "left_line": 878, + "right_line": 878 + }, + { + "left_line": 879, + "right_line": 879 + }, + { + "left_line": 880, + "right_line": 880 + }, + { + "left_line": 881, + "right_line": 881 + }, + { + "left_line": 882, + "right_line": 882 + }, + { + "left_line": 883, + "right_line": 883 + }, + { + "left_line": 884, + "right_line": 884 + }, + { + "left_line": 885, + "right_line": 885 + }, + { + "left_line": 886, + "right_line": 886 + }, + { + "left_line": 887, + "right_line": 887 + }, + { + "left_line": 888, + "right_line": 888 + }, + { + "left_line": 889, + "right_line": 889 + }, + { + "left_line": 890, + "right_line": 890 + }, + { + "left_line": 891, + "right_line": 891 + }, + { + "left_line": 892, + "right_line": 892 + }, + { + "left_line": 893, + "right_line": 893 + }, + { + "left_line": 894, + "right_line": 894 + }, + { + "left_line": 895, + "right_line": 895 + }, + { + "left_line": 896, + "right_line": 896 + }, + { + "left_line": 897, + "right_line": 897 + }, + { + "left_line": 898, + "right_line": 898 + }, + { + "left_line": 899, + "right_line": 899 + }, + { + "left_line": 900, + "right_line": 900 + }, + { + "left_line": 901, + "right_line": 901 + }, + { + "left_line": 902, + "right_line": 902 + }, + { + "left_line": 903, + "right_line": 903 + }, + { + "left_line": 904, + "right_line": 904 + }, + { + "left_line": 905, + "right_line": 905 + }, + { + "left_line": 906, + "right_line": 906 + }, + { + "left_line": 907, + "right_line": 907 + }, + { + "left_line": 908, + "right_line": 908 + }, + { + "left_line": 909, + "right_line": 909 + }, + { + "left_line": 910, + "right_line": 910 + }, + { + "left_line": 911, + "right_line": 911 + }, + { + "left_line": 912, + "right_line": 912 + }, + { + "left_line": 913, + "right_line": 913 + }, + { + "left_line": 914, + "right_line": 914 + }, + { + "left_line": 915, + "right_line": 915 + }, + { + "left_line": 916, + "right_line": 916 + }, + { + "left_line": 917, + "right_line": 917 + }, + { + "left_line": 918, + "right_line": 918 + }, + { + "left_line": 919, + "right_line": 919 + }, + { + "left_line": 920, + "right_line": 920 + }, + { + "left_line": 921, + "right_line": 921 + }, + { + "left_line": 922, + "right_line": 922 + }, + { + "left_line": 923, + "right_line": 923 + }, + { + "left_line": -1, + "right_line": 924 + }, + { + "left_line": -1, + "right_line": 925 + }, + { + "left_line": -1, + "right_line": 926 + }, + { + "left_line": -1, + "right_line": 927 + }, + { + "left_line": -1, + "right_line": 928 + }, + { + "left_line": -1, + "right_line": 929 + }, + { + "left_line": -1, + "right_line": 930 + }, + { + "left_line": -1, + "right_line": 931 + }, + { + "left_line": -1, + "right_line": 932 + }, + { + "left_line": -1, + "right_line": 933 + }, + { + "left_line": -1, + "right_line": 934 + }, + { + "left_line": -1, + "right_line": 935 + }, + { + "left_line": -1, + "right_line": 936 + }, + { + "left_line": -1, + "right_line": 937 + }, + { + "left_line": -1, + "right_line": 938 + }, + { + "left_line": -1, + "right_line": 939 + }, + { + "left_line": -1, + "right_line": 940 + }, + { + "left_line": -1, + "right_line": 941 + }, + { + "left_line": 924, + "right_line": 942 + }, + { + "left_line": 925, + "right_line": 943 + }, + { + "left_line": 926, + "right_line": 944 + }, + { + "left_line": 927, + "right_line": 945 + }, + { + "left_line": 928, + "right_line": 946 + }, + { + "left_line": 929, + "right_line": 947 + }, + { + "left_line": 930, + "right_line": 948 + }, + { + "left_line": 931, + "right_line": 949 + }, + { + "left_line": 932, + "right_line": 950 + }, + { + "left_line": 933, + "right_line": 951 + }, + { + "left_line": 934, + "right_line": 952 + }, + { + "left_line": 935, + "right_line": 953 + }, + { + "left_line": 936, + "right_line": 954 + }, + { + "left_line": 937, + "right_line": 955 + }, + { + "left_line": 938, + "right_line": 956 + }, + { + "left_line": 939, + "right_line": 957 + }, + { + "left_line": 940, + "right_line": 958 + }, + { + "left_line": 941, + "right_line": 959 + }, + { + "left_line": 942, + "right_line": 960 + }, + { + "left_line": 943, + "right_line": 961 + }, + { + "left_line": 944, + "right_line": 962 + }, + { + "left_line": 945, + "right_line": 963 + }, + { + "left_line": 946, + "right_line": 964 + }, + { + "left_line": 947, + "right_line": 965 + }, + { + "left_line": 948, + "right_line": 966 + }, + { + "left_line": 949, + "right_line": 967 + }, + { + "left_line": 950, + "right_line": 968 + }, + { + "left_line": 951, + "right_line": 969 + }, + { + "left_line": 952, + "right_line": 970 + }, + { + "left_line": 953, + "right_line": 971 + }, + { + "left_line": 954, + "right_line": 972 + }, + { + "left_line": 955, + "right_line": 973 + }, + { + "left_line": 956, + "right_line": 974 + }, + { + "left_line": 957, + "right_line": 975 + }, + { + "left_line": 958, + "right_line": 976 + }, + { + "left_line": 959, + "right_line": 977 + }, + { + "left_line": 960, + "right_line": 978 + }, + { + "left_line": 961, + "right_line": 979 + }, + { + "left_line": 962, + "right_line": 980 + }, + { + "left_line": 963, + "right_line": 981 + }, + { + "left_line": 964, + "right_line": 982 + }, + { + "left_line": 965, + "right_line": 983 + }, + { + "left_line": 966, + "right_line": 984 + }, + { + "left_line": 967, + "right_line": 985 + }, + { + "left_line": 968, + "right_line": 986 + }, + { + "left_line": 969, + "right_line": 987 + }, + { + "left_line": 970, + "right_line": 988 + }, + { + "left_line": 971, + "right_line": 989 + }, + { + "left_line": 972, + "right_line": 990 + }, + { + "left_line": 973, + "right_line": 991 + }, + { + "left_line": 974, + "right_line": 992 + }, + { + "left_line": 975, + "right_line": 993 + }, + { + "left_line": 976, + "right_line": 994 + }, + { + "left_line": 977, + "right_line": 995 + }, + { + "left_line": 978, + "right_line": 996 + }, + { + "left_line": 979, + "right_line": 997 + }, + { + "left_line": 980, + "right_line": 998 + }, + { + "left_line": 981, + "right_line": 999 + }, + { + "left_line": 982, + "right_line": 1000 + }, + { + "left_line": 983, + "right_line": 1001 + }, + { + "left_line": 984, + "right_line": 1002 + }, + { + "left_line": 985, + "right_line": 1003 + }, + { + "left_line": 986, + "right_line": 1004 + }, + { + "left_line": 987, + "right_line": 1005 + }, + { + "left_line": 988, + "right_line": 1006 + }, + { + "left_line": 989, + "right_line": 1007 + }, + { + "left_line": 990, + "right_line": 1008 + }, + { + "left_line": 991, + "right_line": 1009 + }, + { + "left_line": 992, + "right_line": 1010 + }, + { + "left_line": 993, + "right_line": 1011 + }, + { + "left_line": 994, + "right_line": 1012 + }, + { + "left_line": 995, + "right_line": 1013 + }, + { + "left_line": 996, + "right_line": 1014 + }, + { + "left_line": 997, + "right_line": 1015 + }, + { + "left_line": 998, + "right_line": 1016 + }, + { + "left_line": 999, + "right_line": 1017 + }, + { + "left_line": 1000, + "right_line": 1018 + }, + { + "left_line": 1001, + "right_line": 1019 + }, + { + "left_line": 1002, + "right_line": 1020 + }, + { + "left_line": 1003, + "right_line": 1021 + }, + { + "left_line": 1004, + "right_line": 1022 + }, + { + "left_line": 1005, + "right_line": 1023 + }, + { + "left_line": 1006, + "right_line": 1024 + }, + { + "left_line": 1007, + "right_line": 1025 + }, + { + "left_line": 1008, + "right_line": 1026 + }, + { + "left_line": 1009, + "right_line": 1027 + }, + { + "left_line": 1010, + "right_line": 1028 + }, + { + "left_line": 1011, + "right_line": 1029 + }, + { + "left_line": 1012, + "right_line": 1030 + }, + { + "left_line": 1013, + "right_line": 1031 + }, + { + "left_line": 1014, + "right_line": 1032 + }, + { + "left_line": 1015, + "right_line": 1033 + }, + { + "left_line": 1016, + "right_line": 1034 + }, + { + "left_line": 1017, + "right_line": 1035 + }, + { + "left_line": 1018, + "right_line": 1036 + }, + { + "left_line": 1019, + "right_line": 1037 + }, + { + "left_line": 1020, + "right_line": 1038 + }, + { + "left_line": 1021, + "right_line": 1039 + }, + { + "left_line": 1022, + "right_line": 1040 + }, + { + "left_line": 1023, + "right_line": 1041 + }, + { + "left_line": 1024, + "right_line": 1042 + }, + { + "left_line": 1025, + "right_line": 1043 + }, + { + "left_line": 1026, + "right_line": 1044 + }, + { + "left_line": 1027, + "right_line": 1045 + }, + { + "left_line": 1028, + "right_line": 1046 + }, + { + "left_line": 1029, + "right_line": 1047 + }, + { + "left_line": 1030, + "right_line": 1048 + }, + { + "left_line": 1031, + "right_line": 1049 + }, + { + "left_line": 1032, + "right_line": 1050 + }, + { + "left_line": 1033, + "right_line": 1051 + }, + { + "left_line": 1034, + "right_line": 1052 + }, + { + "left_line": 1035, + "right_line": 1053 + }, + { + "left_line": 1036, + "right_line": 1054 + }, + { + "left_line": 1037, + "right_line": 1055 + }, + { + "left_line": 1038, + "right_line": 1056 + }, + { + "left_line": 1039, + "right_line": 1057 + }, + { + "left_line": 1040, + "right_line": 1058 + }, + { + "left_line": 1041, + "right_line": 1059 + }, + { + "left_line": 1042, + "right_line": 1060 + }, + { + "left_line": 1043, + "right_line": 1061 + }, + { + "left_line": 1044, + "right_line": 1062 + }, + { + "left_line": 1045, + "right_line": 1063 + }, + { + "left_line": 1046, + "right_line": 1064 + }, + { + "left_line": 1047, + "right_line": 1065 + }, + { + "left_line": 1048, + "right_line": 1066 + }, + { + "left_line": 1049, + "right_line": 1067 + }, + { + "left_line": 1050, + "right_line": 1068 + }, + { + "left_line": 1051, + "right_line": 1069 + }, + { + "left_line": 1052, + "right_line": 1070 + }, + { + "left_line": 1053, + "right_line": 1071 + }, + { + "left_line": 1054, + "right_line": 1072 + }, + { + "left_line": 1055, + "right_line": 1073 + }, + { + "left_line": 1056, + "right_line": 1074 + }, + { + "left_line": 1057, + "right_line": 1075 + }, + { + "left_line": 1058, + "right_line": 1076 + }, + { + "left_line": 1059, + "right_line": 1077 + }, + { + "left_line": 1060, + "right_line": 1078 + }, + { + "left_line": 1061, + "right_line": 1079 + }, + { + "left_line": 1062, + "right_line": 1080 + }, + { + "left_line": 1063, + "right_line": 1081 + }, + { + "left_line": 1064, + "right_line": 1082 + }, + { + "left_line": 1065, + "right_line": 1083 + }, + { + "left_line": 1066, + "right_line": 1084 + }, + { + "left_line": 1067, + "right_line": 1085 + }, + { + "left_line": 1068, + "right_line": 1086 + }, + { + "left_line": 1069, + "right_line": 1087 + }, + { + "left_line": 1070, + "right_line": 1088 + }, + { + "left_line": 1071, + "right_line": 1089 + }, + { + "left_line": 1072, + "right_line": 1090 + }, + { + "left_line": 1073, + "right_line": 1091 + }, + { + "left_line": 1074, + "right_line": 1092 + }, + { + "left_line": 1075, + "right_line": 1093 + }, + { + "left_line": 1076, + "right_line": 1094 + }, + { + "left_line": 1077, + "right_line": 1095 + }, + { + "left_line": 1078, + "right_line": 1096 + }, + { + "left_line": 1079, + "right_line": 1097 + }, + { + "left_line": 1080, + "right_line": 1098 + }, + { + "left_line": 1081, + "right_line": 1099 + }, + { + "left_line": 1082, + "right_line": 1100 + }, + { + "left_line": 1083, + "right_line": 1101 + }, + { + "left_line": 1084, + "right_line": 1102 + }, + { + "left_line": 1085, + "right_line": 1103 + }, + { + "left_line": 1086, + "right_line": 1104 + }, + { + "left_line": 1087, + "right_line": 1105 + }, + { + "left_line": 1088, + "right_line": 1106 + }, + { + "left_line": 1089, + "right_line": 1107 + }, + { + "left_line": 1090, + "right_line": 1108 + }, + { + "left_line": 1091, + "right_line": 1109 + }, + { + "left_line": 1092, + "right_line": 1110 + }, + { + "left_line": 1093, + "right_line": 1111 + }, + { + "left_line": 1094, + "right_line": 1112 + }, + { + "left_line": 1095, + "right_line": 1113 + }, + { + "left_line": 1096, + "right_line": 1114 + }, + { + "left_line": 1097, + "right_line": 1115 + }, + { + "left_line": 1098, + "right_line": 1116 + }, + { + "left_line": 1099, + "right_line": 1117 + }, + { + "left_line": 1100, + "right_line": 1118 + }, + { + "left_line": 1101, + "right_line": 1119 + }, + { + "left_line": 1102, + "right_line": 1120 + }, + { + "left_line": 1103, + "right_line": 1121 + }, + { + "left_line": 1104, + "right_line": 1122 + }, + { + "left_line": 1105, + "right_line": 1123 + }, + { + "left_line": 1106, + "right_line": 1124 + }, + { + "left_line": 1107, + "right_line": 1125 + }, + { + "left_line": 1108, + "right_line": 1126 + }, + { + "left_line": 1109, + "right_line": 1127 + }, + { + "left_line": 1110, + "right_line": 1128 + }, + { + "left_line": 1111, + "right_line": 1129 + }, + { + "left_line": 1112, + "right_line": 1130 + }, + { + "left_line": 1113, + "right_line": 1131 + }, + { + "left_line": 1114, + "right_line": 1132 + }, + { + "left_line": 1115, + "right_line": 1133 + }, + { + "left_line": 1116, + "right_line": 1134 + }, + { + "left_line": 1117, + "right_line": 1135 + }, + { + "left_line": 1118, + "right_line": 1136 + }, + { + "left_line": 1119, + "right_line": 1137 + }, + { + "left_line": 1120, + "right_line": 1138 + }, + { + "left_line": 1121, + "right_line": 1139 + }, + { + "left_line": 1122, + "right_line": 1140 + }, + { + "left_line": 1123, + "right_line": 1141 + }, + { + "left_line": 1124, + "right_line": 1142 + }, + { + "left_line": 1125, + "right_line": 1143 + }, + { + "left_line": 1126, + "right_line": 1144 + }, + { + "left_line": 1127, + "right_line": 1145 + }, + { + "left_line": 1128, + "right_line": 1146 + }, + { + "left_line": 1129, + "right_line": 1147 + }, + { + "left_line": 1130, + "right_line": 1148 + }, + { + "left_line": 1131, + "right_line": 1149 + }, + { + "left_line": 1132, + "right_line": 1150 + }, + { + "left_line": 1133, + "right_line": 1151 + }, + { + "left_line": 1134, + "right_line": 1152 + }, + { + "left_line": 1135, + "right_line": 1153 + }, + { + "left_line": 1136, + "right_line": 1154 + }, + { + "left_line": 1137, + "right_line": 1155 + }, + { + "left_line": 1138, + "right_line": 1156 + }, + { + "left_line": 1139, + "right_line": 1157 + }, + { + "left_line": 1140, + "right_line": 1158 + }, + { + "left_line": 1141, + "right_line": 1159 + }, + { + "left_line": 1142, + "right_line": 1160 + }, + { + "left_line": 1143, + "right_line": 1161 + }, + { + "left_line": 1144, + "right_line": 1162 + }, + { + "left_line": 1145, + "right_line": 1163 + }, + { + "left_line": 1146, + "right_line": 1164 + }, + { + "left_line": 1147, + "right_line": 1165 + }, + { + "left_line": 1148, + "right_line": 1166 + }, + { + "left_line": 1149, + "right_line": 1167 + }, + { + "left_line": 1150, + "right_line": 1168 + }, + { + "left_line": 1151, + "right_line": 1169 + }, + { + "left_line": 1152, + "right_line": 1170 + }, + { + "left_line": 1153, + "right_line": 1171 + }, + { + "left_line": 1154, + "right_line": 1172 + }, + { + "left_line": 1155, + "right_line": 1173 + }, + { + "left_line": 1156, + "right_line": 1174 + }, + { + "left_line": 1157, + "right_line": 1175 + }, + { + "left_line": 1158, + "right_line": 1176 + }, + { + "left_line": 1159, + "right_line": 1177 + }, + { + "left_line": 1160, + "right_line": 1178 + }, + { + "left_line": 1161, + "right_line": 1179 + }, + { + "left_line": 1162, + "right_line": 1180 + }, + { + "left_line": 1163, + "right_line": 1181 + }, + { + "left_line": 1164, + "right_line": 1182 + }, + { + "left_line": 1165, + "right_line": 1183 + }, + { + "left_line": 1166, + "right_line": 1184 + }, + { + "left_line": 1167, + "right_line": 1185 + }, + { + "left_line": 1168, + "right_line": 1186 + }, + { + "left_line": 1169, + "right_line": 1187 + }, + { + "left_line": 1170, + "right_line": 1188 + }, + { + "left_line": 1171, + "right_line": 1189 + }, + { + "left_line": 1172, + "right_line": 1190 + }, + { + "left_line": 1173, + "right_line": 1191 + }, + { + "left_line": 1174, + "right_line": 1192 + }, + { + "left_line": 1175, + "right_line": 1193 + }, + { + "left_line": 1176, + "right_line": 1194 + }, + { + "left_line": 1177, + "right_line": 1195 + }, + { + "left_line": 1178, + "right_line": 1196 + }, + { + "left_line": 1179, + "right_line": 1197 + }, + { + "left_line": 1180, + "right_line": 1198 + }, + { + "left_line": 1181, + "right_line": 1199 + }, + { + "left_line": 1182, + "right_line": 1200 + }, + { + "left_line": 1183, + "right_line": 1201 + }, + { + "left_line": 1184, + "right_line": 1202 + }, + { + "left_line": 1185, + "right_line": 1203 + }, + { + "left_line": 1186, + "right_line": 1204 + }, + { + "left_line": 1187, + "right_line": 1205 + }, + { + "left_line": 1188, + "right_line": 1206 + }, + { + "left_line": 1189, + "right_line": 1207 + }, + { + "left_line": 1190, + "right_line": 1208 + }, + { + "left_line": 1191, + "right_line": 1209 + }, + { + "left_line": 1192, + "right_line": 1210 + }, + { + "left_line": 1193, + "right_line": 1211 + }, + { + "left_line": 1194, + "right_line": 1212 + }, + { + "left_line": 1195, + "right_line": 1213 + }, + { + "left_line": 1196, + "right_line": 1214 + }, + { + "left_line": 1197, + "right_line": 1215 + }, + { + "left_line": 1198, + "right_line": 1216 + }, + { + "left_line": 1199, + "right_line": 1217 + }, + { + "left_line": 1200, + "right_line": 1218 + }, + { + "left_line": 1201, + "right_line": 1219 + }, + { + "left_line": 1202, + "right_line": 1220 + }, + { + "left_line": 1203, + "right_line": 1221 + }, + { + "left_line": 1204, + "right_line": 1222 + }, + { + "left_line": 1205, + "right_line": 1223 + }, + { + "left_line": 1206, + "right_line": 1224 + }, + { + "left_line": 1207, + "right_line": 1225 + }, + { + "left_line": 1208, + "right_line": 1226 + }, + { + "left_line": 1209, + "right_line": 1227 + }, + { + "left_line": 1210, + "right_line": 1228 + }, + { + "left_line": 1211, + "right_line": 1229 + }, + { + "left_line": 1212, + "right_line": 1230 + }, + { + "left_line": 1213, + "right_line": 1231 + }, + { + "left_line": 1214, + "right_line": 1232 + }, + { + "left_line": 1215, + "right_line": 1233 + }, + { + "left_line": 1216, + "right_line": 1234 + }, + { + "left_line": 1217, + "right_line": 1235 + }, + { + "left_line": 1218, + "right_line": 1236 + }, + { + "left_line": 1219, + "right_line": 1237 + }, + { + "left_line": 1220, + "right_line": 1238 + }, + { + "left_line": 1221, + "right_line": 1239 + }, + { + "left_line": 1222, + "right_line": 1240 + }, + { + "left_line": 1223, + "right_line": 1241 + }, + { + "left_line": 1224, + "right_line": 1242 + }, + { + "left_line": 1225, + "right_line": 1243 + }, + { + "left_line": 1226, + "right_line": 1244 + }, + { + "left_line": 1227, + "right_line": 1245 + }, + { + "left_line": 1228, + "right_line": 1246 + }, + { + "left_line": 1229, + "right_line": 1247 + }, + { + "left_line": 1230, + "right_line": 1248 + }, + { + "left_line": 1231, + "right_line": 1249 + }, + { + "left_line": 1232, + "right_line": 1250 + }, + { + "left_line": 1233, + "right_line": 1251 + }, + { + "left_line": 1234, + "right_line": 1252 + }, + { + "left_line": 1235, + "right_line": 1253 + }, + { + "left_line": 1236, + "right_line": 1254 + }, + { + "left_line": 1237, + "right_line": 1255 + }, + { + "left_line": 1238, + "right_line": 1256 + }, + { + "left_line": 1239, + "right_line": 1257 + }, + { + "left_line": 1240, + "right_line": 1258 + }, + { + "left_line": 1241, + "right_line": 1259 + }, + { + "left_line": 1242, + "right_line": 1260 + }, + { + "left_line": 1243, + "right_line": 1261 + }, + { + "left_line": 1244, + "right_line": 1262 + }, + { + "left_line": 1245, + "right_line": 1263 + }, + { + "left_line": 1246, + "right_line": 1264 + }, + { + "left_line": 1247, + "right_line": 1265 + }, + { + "left_line": 1248, + "right_line": 1266 + }, + { + "left_line": 1249, + "right_line": 1267 + }, + { + "left_line": 1250, + "right_line": 1268 + }, + { + "left_line": 1251, + "right_line": 1269 + }, + { + "left_line": 1252, + "right_line": 1270 + }, + { + "left_line": 1253, + "right_line": 1271 + }, + { + "left_line": 1254, + "right_line": 1272 + }, + { + "left_line": 1255, + "right_line": 1273 + }, + { + "left_line": 1256, + "right_line": 1274 + }, + { + "left_line": 1257, + "right_line": 1275 + }, + { + "left_line": 1258, + "right_line": 1276 + }, + { + "left_line": 1259, + "right_line": 1277 + }, + { + "left_line": 1260, + "right_line": 1278 + }, + { + "left_line": 1261, + "right_line": 1279 + }, + { + "left_line": 1262, + "right_line": 1280 + }, + { + "left_line": 1263, + "right_line": 1281 + }, + { + "left_line": 1264, + "right_line": 1282 + }, + { + "left_line": 1265, + "right_line": 1283 + }, + { + "left_line": 1266, + "right_line": 1284 + }, + { + "left_line": 1267, + "right_line": 1285 + }, + { + "left_line": 1268, + "right_line": 1286 + }, + { + "left_line": 1269, + "right_line": 1287 + }, + { + "left_line": 1270, + "right_line": 1288 + }, + { + "left_line": 1271, + "right_line": 1289 + }, + { + "left_line": 1272, + "right_line": 1290 + }, + { + "left_line": 1273, + "right_line": 1291 + }, + { + "left_line": 1274, + "right_line": 1292 + }, + { + "left_line": 1275, + "right_line": 1293 + }, + { + "left_line": 1276, + "right_line": 1294 + }, + { + "left_line": 1277, + "right_line": 1295 + }, + { + "left_line": 1278, + "right_line": 1296 + }, + { + "left_line": 1279, + "right_line": 1297 + }, + { + "left_line": 1280, + "right_line": 1298 + }, + { + "left_line": 1281, + "right_line": 1299 + }, + { + "left_line": 1282, + "right_line": 1300 + }, + { + "left_line": 1283, + "right_line": 1301 + }, + { + "left_line": 1284, + "right_line": 1302 + }, + { + "left_line": 1285, + "right_line": 1303 + }, + { + "left_line": 1286, + "right_line": 1304 + }, + { + "left_line": 1287, + "right_line": 1305 + }, + { + "left_line": 1288, + "right_line": 1306 + }, + { + "left_line": 1289, + "right_line": 1307 + }, + { + "left_line": 1290, + "right_line": 1308 + }, + { + "left_line": 1291, + "right_line": 1309 + }, + { + "left_line": 1292, + "right_line": 1310 + }, + { + "left_line": 1293, + "right_line": 1311 + }, + { + "left_line": 1294, + "right_line": 1312 + }, + { + "left_line": 1295, + "right_line": 1313 + }, + { + "left_line": 1296, + "right_line": 1314 + }, + { + "left_line": 1297, + "right_line": 1315 + }, + { + "left_line": 1298, + "right_line": 1316 + }, + { + "left_line": 1299, + "right_line": 1317 + }, + { + "left_line": 1300, + "right_line": 1318 + }, + { + "left_line": 1301, + "right_line": 1319 + }, + { + "left_line": 1302, + "right_line": 1320 + }, + { + "left_line": 1303, + "right_line": 1321 + }, + { + "left_line": 1304, + "right_line": 1322 + }, + { + "left_line": 1305, + "right_line": 1323 + }, + { + "left_line": 1306, + "right_line": 1324 + }, + { + "left_line": 1307, + "right_line": 1325 + }, + { + "left_line": 1308, + "right_line": 1326 + }, + { + "left_line": 1309, + "right_line": 1327 + }, + { + "left_line": 1310, + "right_line": 1328 + }, + { + "left_line": 1311, + "right_line": 1329 + }, + { + "left_line": 1312, + "right_line": 1330 + }, + { + "left_line": 1313, + "right_line": 1331 + }, + { + "left_line": 1314, + "right_line": 1332 + }, + { + "left_line": 1315, + "right_line": 1333 + }, + { + "left_line": 1316, + "right_line": 1334 + }, + { + "left_line": 1317, + "right_line": 1335 + }, + { + "left_line": 1318, + "right_line": 1336 + }, + { + "left_line": 1319, + "right_line": 1337 + }, + { + "left_line": 1320, + "right_line": 1338 + }, + { + "left_line": 1321, + "right_line": 1339 + }, + { + "left_line": 1322, + "right_line": 1340 + }, + { + "left_line": 1323, + "right_line": 1341 + }, + { + "left_line": 1324, + "right_line": 1342 + }, + { + "left_line": 1325, + "right_line": 1343 + }, + { + "left_line": 1326, + "right_line": 1344 + }, + { + "left_line": 1327, + "right_line": 1345 + }, + { + "left_line": 1328, + "right_line": 1346 + }, + { + "left_line": 1329, + "right_line": 1347 + }, + { + "left_line": 1330, + "right_line": 1348 + }, + { + "left_line": 1331, + "right_line": 1349 + }, + { + "left_line": 1332, + "right_line": 1350 + }, + { + "left_line": 1333, + "right_line": 1351 + }, + { + "left_line": 1334, + "right_line": 1352 + }, + { + "left_line": 1335, + "right_line": 1353 + }, + { + "left_line": 1336, + "right_line": 1354 + }, + { + "left_line": 1337, + "right_line": 1355 + }, + { + "left_line": 1338, + "right_line": 1356 + }, + { + "left_line": 1339, + "right_line": 1357 + }, + { + "left_line": 1340, + "right_line": 1358 + }, + { + "left_line": 1341, + "right_line": 1359 + }, + { + "left_line": 1342, + "right_line": 1360 + }, + { + "left_line": 1343, + "right_line": 1361 + }, + { + "left_line": 1344, + "right_line": 1362 + }, + { + "left_line": 1345, + "right_line": 1363 + }, + { + "left_line": 1346, + "right_line": 1364 + }, + { + "left_line": 1347, + "right_line": 1365 + }, + { + "left_line": 1348, + "right_line": 1366 + }, + { + "left_line": 1349, + "right_line": 1367 + }, + { + "left_line": 1350, + "right_line": 1368 + }, + { + "left_line": 1351, + "right_line": 1369 + }, + { + "left_line": 1352, + "right_line": 1370 + }, + { + "left_line": 1353, + "right_line": 1371 + }, + { + "left_line": 1354, + "right_line": 1372 + }, + { + "left_line": 1355, + "right_line": 1373 + }, + { + "left_line": 1356, + "right_line": 1374 + }, + { + "left_line": 1357, + "right_line": 1375 + }, + { + "left_line": 1358, + "right_line": 1376 + }, + { + "left_line": 1359, + "right_line": 1377 + }, + { + "left_line": 1360, + "right_line": 1378 + }, + { + "left_line": 1361, + "right_line": 1379 + }, + { + "left_line": 1362, + "right_line": 1380 + }, + { + "left_line": 1363, + "right_line": 1381 + }, + { + "left_line": 1364, + "right_line": 1382 + }, + { + "left_line": 1365, + "right_line": 1383 + }, + { + "left_line": 1366, + "right_line": 1384 + }, + { + "left_line": 1367, + "right_line": 1385 + }, + { + "left_line": 1368, + "right_line": 1386 + }, + { + "left_line": 1369, + "right_line": 1387 + }, + { + "left_line": 1370, + "right_line": 1388 + }, + { + "left_line": 1371, + "right_line": 1389 + }, + { + "left_line": 1372, + "right_line": 1390 + }, + { + "left_line": 1373, + "right_line": 1391 + }, + { + "left_line": 1374, + "right_line": 1392 + }, + { + "left_line": 1375, + "right_line": 1393 + }, + { + "left_line": 1376, + "right_line": 1394 + }, + { + "left_line": 1377, + "right_line": 1395 + }, + { + "left_line": 1378, + "right_line": 1396 + }, + { + "left_line": 1379, + "right_line": 1397 + }, + { + "left_line": 1380, + "right_line": 1398 + }, + { + "left_line": 1381, + "right_line": 1399 + }, + { + "left_line": 1382, + "right_line": 1400 + }, + { + "left_line": 1383, + "right_line": 1401 + }, + { + "left_line": 1384, + "right_line": 1402 + }, + { + "left_line": 1385, + "right_line": 1403 + }, + { + "left_line": 1386, + "right_line": 1404 + }, + { + "left_line": 1387, + "right_line": 1405 + }, + { + "left_line": 1388, + "right_line": 1406 + }, + { + "left_line": 1389, + "right_line": 1407 + }, + { + "left_line": 1390, + "right_line": 1408 + }, + { + "left_line": 1391, + "right_line": 1409 + }, + { + "left_line": 1392, + "right_line": 1410 + }, + { + "left_line": 1393, + "right_line": 1411 + }, + { + "left_line": 1394, + "right_line": 1412 + }, + { + "left_line": 1395, + "right_line": 1413 + }, + { + "left_line": 1396, + "right_line": 1414 + }, + { + "left_line": 1397, + "right_line": 1415 + }, + { + "left_line": 1398, + "right_line": 1416 + }, + { + "left_line": 1399, + "right_line": 1417 + }, + { + "left_line": 1400, + "right_line": 1418 + }, + { + "left_line": 1401, + "right_line": 1419 + }, + { + "left_line": 1402, + "right_line": 1420 + }, + { + "left_line": 1403, + "right_line": 1421 + }, + { + "left_line": 1404, + "right_line": 1422 + }, + { + "left_line": 1405, + "right_line": 1423 + }, + { + "left_line": 1406, + "right_line": 1424 + }, + { + "left_line": 1407, + "right_line": 1425 + }, + { + "left_line": 1408, + "right_line": 1426 + }, + { + "left_line": 1409, + "right_line": 1427 + }, + { + "left_line": 1410, + "right_line": 1428 + }, + { + "left_line": 1411, + "right_line": 1429 + }, + { + "left_line": 1412, + "right_line": 1430 + }, + { + "left_line": 1413, + "right_line": 1431 + }, + { + "left_line": 1414, + "right_line": 1432 + }, + { + "left_line": 1415, + "right_line": 1433 + }, + { + "left_line": 1416, + "right_line": 1434 + }, + { + "left_line": 1417, + "right_line": 1435 + }, + { + "left_line": 1418, + "right_line": 1436 + }, + { + "left_line": 1419, + "right_line": 1437 + }, + { + "left_line": 1420, + "right_line": 1438 + }, + { + "left_line": 1421, + "right_line": 1439 + }, + { + "left_line": 1422, + "right_line": 1440 + }, + { + "left_line": 1423, + "right_line": 1441 + }, + { + "left_line": 1424, + "right_line": 1442 + }, + { + "left_line": 1425, + "right_line": 1443 + }, + { + "left_line": 1426, + "right_line": 1444 + }, + { + "left_line": 1427, + "right_line": 1445 + }, + { + "left_line": 1428, + "right_line": 1446 + }, + { + "left_line": 1429, + "right_line": 1447 + }, + { + "left_line": 1430, + "right_line": 1448 + }, + { + "left_line": 1431, + "right_line": 1449 + }, + { + "left_line": 1432, + "right_line": 1450 + }, + { + "left_line": 1433, + "right_line": 1451 + }, + { + "left_line": 1434, + "right_line": 1452 + }, + { + "left_line": 1435, + "right_line": 1453 + }, + { + "left_line": 1436, + "right_line": 1454 + }, + { + "left_line": 1437, + "right_line": 1455 + }, + { + "left_line": 1438, + "right_line": 1456 + }, + { + "left_line": 1439, + "right_line": 1457 + }, + { + "left_line": 1440, + "right_line": 1458 + }, + { + "left_line": 1441, + "right_line": 1459 + }, + { + "left_line": 1442, + "right_line": 1460 + }, + { + "left_line": 1443, + "right_line": 1461 + }, + { + "left_line": 1444, + "right_line": 1462 + }, + { + "left_line": 1445, + "right_line": 1463 + }, + { + "left_line": 1446, + "right_line": 1464 + }, + { + "left_line": 1447, + "right_line": 1465 + }, + { + "left_line": 1448, + "right_line": 1466 + }, + { + "left_line": 1449, + "right_line": 1467 + }, + { + "left_line": 1450, + "right_line": 1468 + }, + { + "left_line": 1451, + "right_line": 1469 + }, + { + "left_line": 1452, + "right_line": 1470 + }, + { + "left_line": 1453, + "right_line": 1471 + }, + { + "left_line": 1454, + "right_line": 1472 + }, + { + "left_line": 1455, + "right_line": 1473 + }, + { + "left_line": 1456, + "right_line": 1474 + }, + { + "left_line": 1457, + "right_line": 1475 + }, + { + "left_line": 1458, + "right_line": 1476 + }, + { + "left_line": 1459, + "right_line": 1477 + }, + { + "left_line": 1460, + "right_line": 1478 + }, + { + "left_line": 1461, + "right_line": 1479 + }, + { + "left_line": 1462, + "right_line": 1480 + }, + { + "left_line": 1463, + "right_line": 1481 + }, + { + "left_line": 1464, + "right_line": 1482 + }, + { + "left_line": 1465, + "right_line": 1483 + }, + { + "left_line": 1466, + "right_line": 1484 + }, + { + "left_line": 1467, + "right_line": 1485 + }, + { + "left_line": 1468, + "right_line": 1486 + }, + { + "left_line": 1469, + "right_line": 1487 + }, + { + "left_line": 1470, + "right_line": 1488 + }, + { + "left_line": 1471, + "right_line": 1489 + }, + { + "left_line": 1472, + "right_line": 1490 + }, + { + "left_line": 1473, + "right_line": 1491 + }, + { + "left_line": 1474, + "right_line": 1492 + }, + { + "left_line": 1475, + "right_line": 1493 + }, + { + "left_line": 1476, + "right_line": 1494 + }, + { + "left_line": 1477, + "right_line": 1495 + }, + { + "left_line": 1478, + "right_line": 1496 + }, + { + "left_line": 1479, + "right_line": 1497 + }, + { + "left_line": 1480, + "right_line": 1498 + }, + { + "left_line": 1481, + "right_line": 1499 + }, + { + "left_line": 1482, + "right_line": 1500 + }, + { + "left_line": 1483, + "right_line": 1501 + }, + { + "left_line": 1484, + "right_line": 1502 + }, + { + "left_line": 1485, + "right_line": 1503 + }, + { + "left_line": 1486, + "right_line": 1504 + }, + { + "left_line": 1487, + "right_line": 1505 + }, + { + "left_line": 1488, + "right_line": 1506 + }, + { + "left_line": 1489, + "right_line": 1507 + }, + { + "left_line": 1490, + "right_line": 1508 + }, + { + "left_line": 1491, + "right_line": 1509 + }, + { + "left_line": 1492, + "right_line": 1510 + }, + { + "left_line": 1493, + "right_line": 1511 + }, + { + "left_line": 1494, + "right_line": 1512 + }, + { + "left_line": 1495, + "right_line": 1513 + }, + { + "left_line": 1496, + "right_line": 1514 + }, + { + "left_line": 1497, + "right_line": 1515 + }, + { + "left_line": 1498, + "right_line": 1516 + }, + { + "left_line": 1499, + "right_line": 1517 + }, + { + "left_line": 1500, + "right_line": 1518 + }, + { + "left_line": 1501, + "right_line": 1519 + }, + { + "left_line": 1502, + "right_line": 1520 + }, + { + "left_line": 1503, + "right_line": 1521 + }, + { + "left_line": 1504, + "right_line": 1522 + }, + { + "left_line": 1505, + "right_line": 1523 + }, + { + "left_line": 1506, + "right_line": 1524 + }, + { + "left_line": 1507, + "right_line": 1525 + }, + { + "left_line": 1508, + "right_line": 1526 + }, + { + "left_line": 1509, + "right_line": 1527 + }, + { + "left_line": 1510, + "right_line": 1528 + }, + { + "left_line": 1511, + "right_line": 1529 + }, + { + "left_line": 1512, + "right_line": 1530 + }, + { + "left_line": 1513, + "right_line": 1531 + }, + { + "left_line": 1514, + "right_line": 1532 + }, + { + "left_line": 1515, + "right_line": 1533 + }, + { + "left_line": 1516, + "right_line": 1534 + }, + { + "left_line": 1517, + "right_line": 1535 + }, + { + "left_line": 1518, + "right_line": 1536 + }, + { + "left_line": 1519, + "right_line": 1537 + }, + { + "left_line": 1520, + "right_line": 1538 + }, + { + "left_line": 1521, + "right_line": 1539 + }, + { + "left_line": 1522, + "right_line": 1540 + }, + { + "left_line": 1523, + "right_line": 1541 + }, + { + "left_line": 1524, + "right_line": 1542 + }, + { + "left_line": 1525, + "right_line": 1543 + }, + { + "left_line": 1526, + "right_line": 1544 + }, + { + "left_line": 1527, + "right_line": 1545 + }, + { + "left_line": 1528, + "right_line": 1546 + }, + { + "left_line": 1529, + "right_line": 1547 + }, + { + "left_line": 1530, + "right_line": 1548 + }, + { + "left_line": 1531, + "right_line": 1549 + }, + { + "left_line": 1532, + "right_line": 1550 + }, + { + "left_line": 1533, + "right_line": 1551 + }, + { + "left_line": 1534, + "right_line": 1552 + }, + { + "left_line": 1535, + "right_line": 1553 + }, + { + "left_line": 1536, + "right_line": 1554 + }, + { + "left_line": 1537, + "right_line": 1555 + }, + { + "left_line": 1538, + "right_line": 1556 + }, + { + "left_line": 1539, + "right_line": 1557 + }, + { + "left_line": 1540, + "right_line": 1558 + }, + { + "left_line": 1541, + "right_line": 1559 + }, + { + "left_line": 1542, + "right_line": 1560 + }, + { + "left_line": 1543, + "right_line": 1561 + }, + { + "left_line": 1544, + "right_line": 1562 + }, + { + "left_line": 1545, + "right_line": 1563 + }, + { + "left_line": 1546, + "right_line": 1564 + }, + { + "left_line": 1547, + "right_line": 1565 + }, + { + "left_line": 1548, + "right_line": 1566 + }, + { + "left_line": 1549, + "right_line": 1567 + }, + { + "left_line": 1550, + "right_line": 1568 + }, + { + "left_line": 1551, + "right_line": 1569 + }, + { + "left_line": 1552, + "right_line": 1570 + }, + { + "left_line": 1553, + "right_line": 1571 + }, + { + "left_line": 1554, + "right_line": 1572 + }, + { + "left_line": 1555, + "right_line": 1573 + }, + { + "left_line": 1556, + "right_line": 1574 + }, + { + "left_line": 1557, + "right_line": 1575 + }, + { + "left_line": 1558, + "right_line": 1576 + }, + { + "left_line": 1559, + "right_line": 1577 + }, + { + "left_line": 1560, + "right_line": 1578 + }, + { + "left_line": 1561, + "right_line": 1579 + }, + { + "left_line": 1562, + "right_line": 1580 + }, + { + "left_line": 1563, + "right_line": 1581 + }, + { + "left_line": 1564, + "right_line": 1582 + }, + { + "left_line": 1565, + "right_line": 1583 + }, + { + "left_line": 1566, + "right_line": 1584 + }, + { + "left_line": 1567, + "right_line": 1585 + }, + { + "left_line": 1568, + "right_line": 1586 + }, + { + "left_line": 1569, + "right_line": 1587 + }, + { + "left_line": 1570, + "right_line": 1588 + }, + { + "left_line": 1571, + "right_line": 1589 + }, + { + "left_line": 1572, + "right_line": 1590 + }, + { + "left_line": 1573, + "right_line": 1591 + }, + { + "left_line": 1574, + "right_line": 1592 + }, + { + "left_line": 1575, + "right_line": 1593 + }, + { + "left_line": 1576, + "right_line": 1594 + }, + { + "left_line": 1577, + "right_line": 1595 + }, + { + "left_line": 1578, + "right_line": 1596 + }, + { + "left_line": 1579, + "right_line": 1597 + }, + { + "left_line": 1580, + "right_line": 1598 + }, + { + "left_line": 1581, + "right_line": 1599 + }, + { + "left_line": 1582, + "right_line": 1600 + }, + { + "left_line": 1583, + "right_line": 1601 + }, + { + "left_line": 1584, + "right_line": 1602 + }, + { + "left_line": 1585, + "right_line": 1603 + }, + { + "left_line": 1586, + "right_line": 1604 + }, + { + "left_line": 1587, + "right_line": 1605 + }, + { + "left_line": 1588, + "right_line": 1606 + }, + { + "left_line": 1589, + "right_line": 1607 + }, + { + "left_line": 1590, + "right_line": 1608 + }, + { + "left_line": 1591, + "right_line": 1609 + }, + { + "left_line": 1592, + "right_line": 1610 + }, + { + "left_line": 1593, + "right_line": 1611 + }, + { + "left_line": 1594, + "right_line": 1612 + }, + { + "left_line": 1595, + "right_line": 1613 + }, + { + "left_line": 1596, + "right_line": 1614 + }, + { + "left_line": 1597, + "right_line": 1615 + }, + { + "left_line": 1598, + "right_line": 1616 + }, + { + "left_line": 1599, + "right_line": 1617 + }, + { + "left_line": 1600, + "right_line": 1618 + }, + { + "left_line": 1601, + "right_line": 1619 + }, + { + "left_line": 1602, + "right_line": 1620 + }, + { + "left_line": 1603, + "right_line": 1621 + }, + { + "left_line": 1604, + "right_line": 1622 + }, + { + "left_line": 1605, + "right_line": 1623 + }, + { + "left_line": 1606, + "right_line": 1624 + }, + { + "left_line": 1607, + "right_line": 1625 + }, + { + "left_line": 1608, + "right_line": 1626 + }, + { + "left_line": 1609, + "right_line": 1627 + }, + { + "left_line": 1610, + "right_line": 1628 + }, + { + "left_line": 1611, + "right_line": 1629 + }, + { + "left_line": 1612, + "right_line": 1630 + }, + { + "left_line": 1613, + "right_line": 1631 + }, + { + "left_line": 1614, + "right_line": 1632 + }, + { + "left_line": 1615, + "right_line": 1633 + }, + { + "left_line": 1616, + "right_line": 1634 + }, + { + "left_line": 1617, + "right_line": 1635 + }, + { + "left_line": 1618, + "right_line": 1636 + }, + { + "left_line": 1619, + "right_line": 1637 + }, + { + "left_line": 1620, + "right_line": 1638 + }, + { + "left_line": 1621, + "right_line": 1639 + }, + { + "left_line": 1622, + "right_line": 1640 + }, + { + "left_line": 1623, + "right_line": 1641 + }, + { + "left_line": 1624, + "right_line": 1642 + }, + { + "left_line": 1625, + "right_line": 1643 + }, + { + "left_line": 1626, + "right_line": 1644 + }, + { + "left_line": 1627, + "right_line": 1645 + }, + { + "left_line": 1628, + "right_line": 1646 + }, + { + "left_line": 1629, + "right_line": 1647 + }, + { + "left_line": 1630, + "right_line": 1648 + }, + { + "left_line": 1631, + "right_line": 1649 + }, + { + "left_line": 1632, + "right_line": 1650 + }, + { + "left_line": 1633, + "right_line": 1651 + }, + { + "left_line": 1634, + "right_line": 1652 + }, + { + "left_line": 1635, + "right_line": 1653 + }, + { + "left_line": 1636, + "right_line": 1654 + }, + { + "left_line": 1637, + "right_line": 1655 + }, + { + "left_line": 1638, + "right_line": 1656 + }, + { + "left_line": 1639, + "right_line": 1657 + }, + { + "left_line": 1640, + "right_line": 1658 + }, + { + "left_line": 1641, + "right_line": 1659 + }, + { + "left_line": 1642, + "right_line": 1660 + }, + { + "left_line": 1643, + "right_line": 1661 + }, + { + "left_line": 1644, + "right_line": 1662 + }, + { + "left_line": 1645, + "right_line": 1663 + }, + { + "left_line": 1646, + "right_line": 1664 + }, + { + "left_line": 1647, + "right_line": 1665 + }, + { + "left_line": 1648, + "right_line": 1666 + }, + { + "left_line": 1649, + "right_line": 1667 + }, + { + "left_line": 1650, + "right_line": 1668 + }, + { + "left_line": 1651, + "right_line": 1669 + }, + { + "left_line": 1652, + "right_line": 1670 + }, + { + "left_line": 1653, + "right_line": 1671 + }, + { + "left_line": 1654, + "right_line": 1672 + }, + { + "left_line": 1655, + "right_line": 1673 + }, + { + "left_line": 1656, + "right_line": 1674 + }, + { + "left_line": 1657, + "right_line": 1675 + }, + { + "left_line": 1658, + "right_line": 1676 + }, + { + "left_line": 1659, + "right_line": 1677 + }, + { + "left_line": 1660, + "right_line": 1678 + }, + { + "left_line": 1661, + "right_line": 1679 + }, + { + "left_line": 1662, + "right_line": 1680 + }, + { + "left_line": 1663, + "right_line": 1681 + }, + { + "left_line": 1664, + "right_line": 1682 + }, + { + "left_line": 1665, + "right_line": 1683 + }, + { + "left_line": 1666, + "right_line": 1684 + }, + { + "left_line": 1667, + "right_line": 1685 + }, + { + "left_line": 1668, + "right_line": 1686 + }, + { + "left_line": 1669, + "right_line": 1687 + }, + { + "left_line": 1670, + "right_line": 1688 + }, + { + "left_line": 1671, + "right_line": 1689 + }, + { + "left_line": 1672, + "right_line": 1690 + }, + { + "left_line": 1673, + "right_line": 1691 + }, + { + "left_line": 1674, + "right_line": 1692 + }, + { + "left_line": 1675, + "right_line": 1693 + }, + { + "left_line": 1676, + "right_line": 1694 + }, + { + "left_line": 1677, + "right_line": 1695 + }, + { + "left_line": 1678, + "right_line": 1696 + }, + { + "left_line": 1679, + "right_line": 1697 + }, + { + "left_line": 1680, + "right_line": 1698 + }, + { + "left_line": 1681, + "right_line": 1699 + }, + { + "left_line": 1682, + "right_line": 1700 + }, + { + "left_line": 1683, + "right_line": 1701 + }, + { + "left_line": 1684, + "right_line": 1702 + }, + { + "left_line": 1685, + "right_line": 1703 + }, + { + "left_line": 1686, + "right_line": 1704 + }, + { + "left_line": 1687, + "right_line": 1705 + }, + { + "left_line": 1688, + "right_line": 1706 + }, + { + "left_line": 1689, + "right_line": 1707 + }, + { + "left_line": 1690, + "right_line": 1708 + }, + { + "left_line": 1691, + "right_line": 1709 + }, + { + "left_line": 1692, + "right_line": 1710 + }, + { + "left_line": 1693, + "right_line": 1711 + }, + { + "left_line": 1694, + "right_line": 1712 + }, + { + "left_line": 1695, + "right_line": 1713 + }, + { + "left_line": 1696, + "right_line": 1714 + }, + { + "left_line": 1697, + "right_line": 1715 + }, + { + "left_line": 1698, + "right_line": 1716 + }, + { + "left_line": 1699, + "right_line": 1717 + }, + { + "left_line": 1700, + "right_line": 1718 + }, + { + "left_line": 1701, + "right_line": 1719 + }, + { + "left_line": 1702, + "right_line": 1720 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/metadata.json b/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/metadata.json new file mode 100644 index 0000000..1b585aa --- /dev/null +++ b/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "e81490eea468f3dce6665490508b6ff7c3625a9a", + "commit": "91d89929011f4b1c909b89d7b94bccdd769f18ad", + "message": "Add regression test for content-type leak in pass-block routing\n\nGuards the fix in the previous commit: asserts that a content-type set by a", + "author": "Sean Floyd", + "date": "2026-06-25T12:40:33-04:00", + "language": "ruby", + "files_changed": 1, + "insertions": 18, + "deletions": 0 +} diff --git a/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/new.rb b/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/new.rb new file mode 100644 index 0000000..3261f73 --- /dev/null +++ b/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/new.rb @@ -0,0 +1,1720 @@ +require_relative 'test_helper' + +# Helper method for easy route pattern matching testing +def route_def(pattern) + mock_app { get(pattern) { } } +end + +class PatternLookAlike + def to_pattern(*) + self + end + + def params(input) + { "one" => +"this", "two" => +"is", "three" => +"a", "four" => +"test" } + end +end + +class RoutingTest < Minitest::Test + %w[get put post delete options patch link unlink].each do |verb| + it "defines #{verb.upcase} request handlers with #{verb}" do + mock_app { + send verb, '/hello' do + 'Hello World' + end + } + + request = Rack::MockRequest.new(@app) + response = request.request(verb.upcase, '/hello', {}) + assert response.ok? + assert_equal 'Hello World', response.body + end + end + + it "defines HEAD request handlers with HEAD" do + mock_app { + head '/hello' do + response['X-Hello'] = 'World!' + 'remove me' + end + } + + request = Rack::MockRequest.new(@app) + response = request.request('HEAD', '/hello', {}) + assert response.ok? + assert_equal 'World!', response['X-Hello'] + assert_equal '', response.body + end + + it "400s when request params contain conflicting types" do + mock_app { + get('/foo') { } + } + + request = Rack::MockRequest.new(@app) + response = request.request('GET', '/foo?bar=&bar[]=', {}) + assert response.bad_request? + end + + it "404s when no route satisfies the request" do + mock_app { + get('/foo') { } + } + get '/bar' + assert_equal 404, status + end + + it "404s and sets X-Cascade header when no route satisfies the request" do + mock_app { + get('/foo') { } + } + get '/bar' + assert_equal 404, status + assert_equal 'pass', response.headers['X-Cascade'] + end + + it "404s and does not set X-Cascade header when no route satisfies the request and x_cascade has been disabled" do + mock_app { + disable :x_cascade + get('/foo') { } + } + get '/bar' + assert_equal 404, status + assert_nil response.headers['X-Cascade'] + end + + + it "allows using unicode" do + mock_app do + get('/föö') { } + end + get '/f%C3%B6%C3%B6' + assert_equal 200, status + end + + it "it handles encoded slashes correctly" do + mock_app { + set :protection, :except => :path_traversal + get("/:a") { |a| a } + } + get '/foo%2Fbar' + assert_equal 200, status + assert_body "foo/bar" + end + + it "it handles encoded colons correctly" do + mock_app { + get("/\\:") { 'a' } + get("/a/\\:") { 'b' } + get("/a/\\:/b") { 'c' } + get("/a/b\\:") { 'd' } + get("/a/b\\: ") { 'e' } + } + get '/:' + assert_equal 200, status + assert_body "a" + get '/%3a' + assert_equal 200, status + assert_body "a" + + get '/a/:' + assert_equal 200, status + assert_body "b" + get '/a/%3a' + assert_equal 200, status + assert_body "b" + + get '/a/:/b' + assert_equal 200, status + assert_body "c" + get '/a/%3A/b' + assert_equal 200, status + assert_body "c" + + get '/a/b:' + assert_equal 200, status + assert_body "d" + get '/a/b%3a' + assert_equal 200, status + assert_body "d" + + get '/a/b%3a%20' + assert_equal 200, status + assert_body "e" + get '/a/b%3a+' + assert_equal 200, status + assert_body "e" + end + + it "overrides the content-type in error handlers" do + mock_app { + before { content_type 'text/plain' } + error Sinatra::NotFound do + content_type "text/html" + "

Not Found

" + end + } + + get '/foo' + assert_equal 404, status + assert_equal 'text/html;charset=utf-8', response["Content-Type"] + assert_equal "

Not Found

", response.body + end + + it "recalculates body length correctly for 404 response" do + mock_app { + get '/' do + @response["Content-Length"] = "30" + raise Sinatra::NotFound + end + } + + get "/" + assert_equal "18", response["Content-Length"] + assert_equal 404, status + end + + it "captures the exception message of a raised NotFound" do + mock_app { + get '/' do + raise Sinatra::NotFound, "This is not a drill" + end + } + + get "/" + assert_equal "19", response["Content-Length"] + assert_equal 404, status + assert_equal "This is not a drill", response.body + end + + it "captures the exception message of a raised BadRequest" do + mock_app { + get '/' do + raise Sinatra::BadRequest, "This is not a drill either" + end + } + + get "/" + assert_equal "26", response["Content-Length"] + assert_equal 400, status + assert_equal "This is not a drill either", response.body + end + + it "captures the custom exception message of a BadRequest" do + mock_app { + get('/') {} + + error Sinatra::BadRequest do + 'This is not a drill either' + end + } + + get "/", "foo" => "", "foo[]" => "" + assert_equal "26", response["Content-Length"] + assert_equal 400, status + assert_equal "This is not a drill either", response.body + end + + it "returns empty when unmatched with any regex captures" do + mock_app do + before do + # noop + end + + get '/hello' do + params.to_s + end + end + + assert get('/hello').ok? + assert_body '{}' + end + + it "uses 404 error handler for not matching route" do + mock_app { + not_found do + "nf" + end + error 404 do + "e" + end + } + + get "/" + assert_equal "e", body + assert_equal 404, status + end + + it 'matches empty PATH_INFO to "/" if no route is defined for ""' do + mock_app do + get '/' do + 'worked' + end + end + + get '/', {}, "PATH_INFO" => "" + assert ok? + assert_equal 'worked', body + rescue Rack::Lint::LintError => error + # Temporary fix for https://github.com/sinatra/sinatra/issues/2113 + skip error.message + end + + it 'matches empty PATH_INFO to "" if a route is defined for ""' do + mock_app do + disable :protection + + get '/' do + 'did not work' + end + + get '' do + 'worked' + end + end + + get '/', {}, "PATH_INFO" => "" + assert ok? + assert_equal 'worked', body + rescue Rack::Lint::LintError => error + # Temporary fix for https://github.com/sinatra/sinatra/issues/2113 + skip error.message + end + + it 'takes multiple definitions of a route' do + mock_app { + user_agent(/Foo/) + get '/foo' do + 'foo' + end + + get '/foo' do + 'not foo' + end + } + + get '/foo', {}, 'HTTP_USER_AGENT' => 'Foo' + assert ok? + assert_equal 'foo', body + + get '/foo' + assert ok? + assert_equal 'not foo', body + end + + it "exposes params with indifferent hash" do + mock_app { + get '/:foo' do + assert_equal 'bar', params['foo'] + assert params.has_key?('foo') + assert_equal 'bar', params[:foo] + assert params.has_key?(:foo) + 'well, alright' + end + } + get '/bar' + assert_equal 'well, alright', body + end + + it "handles params without a value" do + mock_app { + get '/' do + assert_nil params.fetch('foo') + "Given: #{params.keys.sort.join(',')}" + end + } + get '/?foo' + assert_equal 'Given: foo', body + end + + it "merges named params and query string params in params" do + mock_app { + get '/:foo' do + assert_equal 'bar', params['foo'] + assert_equal 'biz', params['baz'] + end + } + get '/bar?baz=biz' + assert ok? + end + + it "supports named params like /hello/:person" do + mock_app { + get '/hello/:person' do + "Hello #{params['person']}" + end + } + get '/hello/Frank' + assert_equal 'Hello Frank', body + end + + it "supports optional named params like /?:foo?/?:bar?" do + mock_app { + get '/?:foo?/?:bar?' do + "foo=#{params[:foo]};bar=#{params[:bar]}" + end + } + + get '/hello/world' + assert ok? + assert_equal "foo=hello;bar=world", body + + get '/hello' + assert ok? + assert_equal "foo=hello;bar=", body + + get '/hello?bar=baz' + assert ok? + assert_equal "foo=hello;bar=baz", body + + get '/' + assert ok? + assert_equal "foo=;bar=", body + end + + it "uses the default encoding for named params" do + mock_app { + set :default_encoding ,'ISO-8859-1' + + get '/:foo/:bar' do + "foo=#{params[:foo].encoding};bar=#{params[:bar].encoding}" + end + } + get '/f%C3%B6%C3%B6/b%C3%B6%C3%B6' + assert ok? + assert_equal 'foo=ISO-8859-1;bar=ISO-8859-1', body + end + + it "supports named captures like %r{/hello/(?[^/?#]+)}" do + mock_app { + get Regexp.new('/hello/(?[^/?#]+)') do + "Hello #{params['person']}" + end + } + get '/hello/Frank' + assert_equal 'Hello Frank', body + end + + it "supports optional named captures like %r{/page(?.[^/?#]+)?}" do + mock_app { + get Regexp.new('/page(?.[^/?#]+)?') do + "format=#{params[:format]}" + end + } + + get '/page.html' + assert ok? + assert_equal "format=.html", body + + get '/page.xml' + assert ok? + assert_equal "format=.xml", body + + get '/page' + assert ok? + assert_equal "format=", body + end + + it 'uses the default encoding for named captures' do + mock_app { + set :default_encoding ,'ISO-8859-1' + + get Regexp.new('/page(?.[^/?#]+)?') do + "format=#{params[:format].encoding};captures=#{params[:captures][0].encoding}" + end + } + get '/page.f%C3%B6' + assert ok? + assert_equal 'format=ISO-8859-1;captures=ISO-8859-1', body + end + + it 'does not concatenate params with the same name' do + mock_app { get('/:foo') { params[:foo] } } + get '/a?foo=b' + assert_body 'a' + end + + it "supports single splat params like /*" do + mock_app { + get '/*' do + assert params['splat'].kind_of?(Array) + params['splat'].join "\n" + end + } + + get '/foo' + assert_equal "foo", body + + get '/foo/bar/baz' + assert_equal "foo/bar/baz", body + end + + it "supports mixing multiple splat params like /*/foo/*/*" do + mock_app { + get '/*/foo/*/*' do + assert params['splat'].kind_of?(Array) + params['splat'].join "\n" + end + } + + get '/bar/foo/bling/baz/boom' + assert_equal "bar\nbling\nbaz/boom", body + + get '/bar/foo/baz' + assert not_found? + end + + it "supports mixing named and splat params like /:foo/*" do + mock_app { + get '/:foo/*' do + assert_equal 'foo', params['foo'] + assert_equal ['bar/baz'], params['splat'] + end + } + + get '/foo/bar/baz' + assert ok? + end + + it "matches a dot ('.') as part of a named param" do + mock_app { + get '/:foo/:bar' do + params[:foo] + end + } + + get '/user@example.com/name' + assert_equal 200, response.status + assert_equal 'user@example.com', body + end + + it "matches a literal dot ('.') outside of named params" do + mock_app { + get '/:file.:ext' do + assert_equal 'pony', params[:file] + assert_equal 'jpg', params[:ext] + 'right on' + end + } + + get '/pony.jpg' + assert_equal 200, response.status + assert_equal 'right on', body + end + + it "literally matches dot in paths" do + route_def '/test.bar' + + get '/test.bar' + assert ok? + get 'test0bar' + assert not_found? + end + + it "literally matches dollar sign in paths" do + route_def '/test$/' + + get '/test$/' + assert ok? + end + + it "literally matches plus sign in paths" do + route_def '/te+st/' + + get '/te%2Bst/' + assert ok? + get '/teeeeeeest/' + assert not_found? + end + + it "does not convert plus sign into space as the value of a named param" do + mock_app do + get '/:test' do + params["test"] + end + end + get '/bob+ross' + assert ok? + assert_equal 'bob+ross', body + end + + it "literally matches parens in paths when escaped" do + route_def '/test\(bar\)/' + + get '/test(bar)/' + assert ok? + end + + it "supports basic nested params" do + mock_app { + get '/hi' do + params["person"]["name"] + end + } + + get "/hi?person[name]=John+Doe" + assert ok? + assert_equal "John Doe", body + end + + it "exposes nested params with indifferent hash" do + mock_app { + get '/testme' do + assert_equal 'baz', params['bar']['foo'] + assert_equal 'baz', params['bar'][:foo] + 'well, alright' + end + } + get '/testme?bar[foo]=baz' + assert_equal 'well, alright', body + end + + it "exposes params nested within arrays with indifferent hash" do + mock_app { + get '/testme' do + assert_equal 'baz', params['bar'][0]['foo'] + assert_equal 'baz', params['bar'][0][:foo] + 'well, alright' + end + } + get '/testme?bar[][foo]=baz' + assert_equal 'well, alright', body + end + + it "supports arrays within params" do + mock_app { + get '/foo' do + assert_equal ['A', 'B'], params['bar'] + 'looks good' + end + } + get '/foo?bar[]=A&bar[]=B' + assert ok? + assert_equal 'looks good', body + end + + it "supports deeply nested params" do + expected_params = { + "emacs" => { + "map" => { "goto-line" => "M-g g" }, + "version" => "22.3.1" + }, + "browser" => { + "firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}}, + "chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}} + }, + "paste" => {"name"=>"hello world", "syntax"=>"ruby"} + } + mock_app { + get '/foo' do + assert_equal expected_params, params + 'looks good' + end + } + get '/foo', expected_params + assert ok? + assert_equal 'looks good', body + end + + it "preserves non-nested params" do + mock_app { + get '/foo' do + assert_equal "2", params["article_id"] + assert_equal "awesome", params['comment']['body'] + assert_nil params['comment[body]'] + 'looks good' + end + } + + get '/foo?article_id=2&comment[body]=awesome' + assert ok? + assert_equal 'looks good', body + end + + it "matches paths that include spaces encoded with %20" do + mock_app { + get '/path with spaces' do + 'looks good' + end + } + + get '/path%20with%20spaces' + assert ok? + assert_equal 'looks good', body + end + + it "matches paths that include spaces encoded with +" do + mock_app { + get '/path with spaces' do + 'looks good' + end + } + + get '/path+with+spaces' + assert ok? + assert_equal 'looks good', body + end + + it "matches paths that include ampersands" do + mock_app { + get '/:name' do + 'looks good' + end + } + + get '/foo&bar' + assert ok? + assert_equal 'looks good', body + end + + it "URL decodes named parameters and splats" do + mock_app { + get '/:foo/*' do + assert_equal 'hello world', params['foo'] + assert_equal ['how are you'], params['splat'] + nil + end + } + + get '/hello%20world/how%20are%20you' + assert ok? + end + + it 'unescapes named parameters and splats' do + mock_app { + get '/:foo/*' do |a, b| + assert_equal "foo\xE2\x80\x8Cbar", params['foo'] + assert_predicate params['foo'], :valid_encoding? + + assert_equal ["bar\xE2\x80\x8Cbaz"], params['splat'] + end + } + + get '/foo%e2%80%8cbar/bar%e2%80%8cbaz' + assert ok? + end + + it 'supports regular expressions' do + mock_app { + get(/\/foo...\/bar/) do + 'Hello World' + end + } + + get '/foooom/bar' + assert ok? + assert_equal 'Hello World', body + end + + it 'unescapes regular expression captures' do + mock_app { + get(/\/foo\/(.+)/) do |path| + path + end + } + + get '/foo/bar%e2%80%8cbaz' + assert ok? + assert_equal "bar\xE2\x80\x8Cbaz", body + assert_predicate body, :valid_encoding? + end + + it 'makes regular expression captures available in params[:captures]' do + mock_app { + get(/\/fo(.*)\/ba(.*)/) do + assert_equal ['orooomma', 'f'], params[:captures] + 'right on' + end + } + + get '/foorooomma/baf' + assert ok? + assert_equal 'right on', body + end + + it 'makes regular expression captures available in params[:captures] for concatenated routes' do + with_regexp = Mustermann.new('/prefix') + Mustermann.new("/fo(.*)/ba(.*)", type: :regexp) + without_regexp = Mustermann.new('/prefix', type: :identity) + Mustermann.new('/baz') + mock_app { + get(with_regexp) do + assert_equal ['orooomma', 'f'], params[:captures] + 'right on' + end + get(without_regexp) do + assert !params.keys.include?(:captures) + 'no captures here' + end + } + + get '/prefix/foorooomma/baf' + assert ok? + assert_equal 'right on', body + + get '/prefix/baz' + assert ok? + assert_equal 'no captures here', body + end + + it 'supports regular expression look-alike routes' do + mock_app { + get(PatternLookAlike.new) do + assert_equal 'this', params[:one] + assert_equal 'is', params[:two] + assert_equal 'a', params[:three] + assert_equal 'test', params[:four] + 'right on' + end + } + + get '/this/is/a/test/' + assert ok? + assert_equal 'right on', body + end + + it 'raises a TypeError when pattern is not a String or Regexp' do + assert_raises(TypeError) { + mock_app { get(42){} } + } + end + + it "returns response immediately on halt" do + mock_app { + get '/' do + halt 'Hello World' + 'Boo-hoo World' + end + } + + get '/' + assert ok? + assert_equal 'Hello World', body + end + + it "halts with a response tuple" do + mock_app { + get '/' do + halt 295, {'Content-Type' => 'text/plain'}, 'Hello World' + end + } + + get '/' + assert_equal 295, status + assert_equal 'text/plain', response['Content-Type'] + assert_equal 'Hello World', body + end + + it "halts with an array of strings" do + mock_app { + get '/' do + halt %w[Hello World How Are You] + end + } + + get '/' + assert_equal 'HelloWorldHowAreYou', body + end + + it 'sets response.status with halt' do + status_was = nil + mock_app do + after { status_was = status } + get('/') { halt 500, 'error' } + end + get '/' + assert_status 500 + assert_equal 500, status_was + end + + it "transitions to the next matching route on pass" do + mock_app { + get '/:foo' do + pass + 'Hello Foo' + end + + get '/*' do + assert !params.include?('foo') + 'Hello World' + end + } + + get '/bar' + assert ok? + assert_equal 'Hello World', body + end + + it "makes original request params available in error handler" do + mock_app { + disable :raise_errors + + get '/:foo' do + raise ArgumentError, "foo" + end + + error do + "Hello #{params['foo']}2" + end + } + + get '/bar' + assert_equal 'Hello bar2', body + end + + it "transitions to 404 when passed and no subsequent route matches" do + mock_app { + get '/:foo' do + pass + 'Hello Foo' + end + } + + get '/bar' + assert not_found? + end + + it "transitions to 404 and sets X-Cascade header when passed and no subsequent route matches" do + mock_app { + get '/:foo' do + pass + 'Hello Foo' + end + + get '/bar' do + 'Hello Bar' + end + } + + get '/foo' + assert not_found? + assert_equal 'pass', response.headers['X-Cascade'] + end + + it "uses optional block passed to pass as route block if no other route is found" do + mock_app { + get "/" do + pass do + "this" + end + "not this" + end + } + + get "/" + assert ok? + assert "this", body + end + + it "uses optional block passed to pass as route block if no other route is found and superclass has non-matching routes" do + base = Class.new(Sinatra::Base) + base.get('/foo') { 'foo in baseclass' } + + mock_app(base) { + get "/" do + pass do + "this" + end + "not this" + end + } + + get "/" + assert_equal 200, status + assert "this", body + end + + it "does not leak a passed route's content-type into the pass block" do + mock_app { + get('/a') do + content_type :json + pass { 'from pass block' } + end + get('/b') { 'b' } # later, non-matching route for the same verb + } + + get '/a' + assert_equal 200, status + assert_equal 'from pass block', body + # The content-type set by the passed route must not survive into the pass + # block: set-based routing iterates only matching routes, so route! must + # clear it before the fallthrough just as the legacy all-routes scan did. + assert_equal 'text/html;charset=utf-8', response.headers['content-type'] + end + + it "passes when matching condition returns false" do + mock_app { + condition { params[:foo] == 'bar' } + get '/:foo' do + 'Hello World' + end + } + + get '/bar' + assert ok? + assert_equal 'Hello World', body + + get '/foo' + assert not_found? + end + + it "does not pass when matching condition returns nil" do + mock_app { + condition { nil } + get '/:foo' do + 'Hello World' + end + } + + get '/bar' + assert ok? + assert_equal 'Hello World', body + end + + it "passes to next route when condition calls pass explicitly" do + mock_app { + condition { pass unless params[:foo] == 'bar' } + get '/:foo' do + 'Hello World' + end + } + + get '/bar' + assert ok? + assert_equal 'Hello World', body + + get '/foo' + assert not_found? + end + + it "passes to the next route when host_name does not match" do + mock_app { + host_name 'example.com' + get '/foo' do + 'Hello World' + end + } + get '/foo' + assert not_found? + + get '/foo', {}, { 'HTTP_HOST' => 'example.com' } + assert_equal 200, status + assert_equal 'Hello World', body + end + + it "passes to the next route when user_agent does not match" do + mock_app { + user_agent(/Foo/) + get '/foo' do + 'Hello World' + end + } + get '/foo' + assert not_found? + + get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' } + assert_equal 200, status + assert_equal 'Hello World', body + end + + it "treats missing user agent like an empty string" do + mock_app do + user_agent(/.*/) + get '/' do + "Hello World" + end + end + get '/' + assert_equal 200, status + assert_equal 'Hello World', body + end + + it "makes captures in user agent pattern available in params[:agent]" do + mock_app { + user_agent(/Foo (.*)/) + get '/foo' do + 'Hello ' + params[:agent].first + end + } + get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' } + assert_equal 200, status + assert_equal 'Hello Bar', body + end + + it 'matches mime_types with dots, hyphens and plus signs' do + mime_types = %w( + application/atom+xml + application/ecmascript + application/EDI-X12 + application/EDIFACT + application/json + application/javascript + application/octet-stream + application/ogg + application/pdf + application/postscript + application/rdf+xml + application/rss+xml + application/soap+xml + application/font-woff + application/xhtml+xml + application/xml + application/xml-dtd + application/xop+xml + application/zip + application/gzip + audio/basic + audio/L24 + audio/mp4 + audio/mpeg + audio/ogg + audio/vorbis + audio/vnd.rn-realaudio + audio/vnd.wave + audio/webm + image/gif + image/jpeg + image/pjpeg + image/png + image/svg+xml + image/tiff + image/vnd.microsoft.icon + message/http + message/imdn+xml + message/partial + message/rfc822 + model/example + model/iges + model/mesh + model/vrml + model/x3d+binary + model/x3d+vrml + model/x3d+xml + multipart/mixed + multipart/alternative + multipart/related + multipart/form-data + multipart/signed + multipart/encrypted + text/cmd + text/css + text/csv + text/html + text/javascript + application/javascript + text/plain + text/vcard + text/xml + video/mpeg + video/mp4 + video/ogg + video/quicktime + video/webm + video/x-matroska + video/x-ms-wmv + video/x-flv + application/vnd.oasis.opendocument.text + application/vnd.oasis.opendocument.spreadsheet + application/vnd.oasis.opendocument.presentation + application/vnd.oasis.opendocument.graphics + application/vnd.ms-excel + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + application/vnd.ms-powerpoint + application/vnd.openxmlformats-officedocument.presentationml.presentation + application/vnd.openxmlformats-officedocument.wordprocessingml.document + application/vnd.mozilla.xul+xml + application/vnd.google-earth.kml+xml + application/x-deb + application/x-dvi + application/x-font-ttf + application/x-javascript + application/x-latex + application/x-mpegURL + application/x-rar-compressed + application/x-shockwave-flash + application/x-stuffit + application/x-tar + application/x-www-form-urlencoded + application/x-xpinstall + audio/x-aac + audio/x-caf + image/x-xcf + text/x-gwt-rpc + text/x-jquery-tmpl + application/x-pkcs12 + application/x-pkcs12 + application/x-pkcs7-certificates + application/x-pkcs7-certificates + application/x-pkcs7-certreqresp + application/x-pkcs7-mime + application/x-pkcs7-mime + application/x-pkcs7-signature + ) + + mime_types.each { |mime_type| assert mime_type.match(Sinatra::Request::HEADER_VALUE_WITH_PARAMS) } + end + + it "filters by accept header" do + mock_app { + get '/', :provides => :xml do + env['HTTP_ACCEPT'] + end + get '/foo', :provides => :html do + env['HTTP_ACCEPT'] + end + get '/stream', :provides => 'text/event-stream' do + env['HTTP_ACCEPT'] + end + } + + get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' } + assert ok? + assert_equal 'application/xml', body + assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type'] + + get '/', {}, {} + assert ok? + assert_equal '', body + assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type'] + + get '/', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal '*/*', body + assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type'] + + get '/', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' } + assert !ok? + + get '/foo', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' } + assert ok? + assert_equal 'text/html;q=0.9', body + + get '/foo', {}, { 'HTTP_ACCEPT' => '' } + assert ok? + assert_equal '', body + + get '/foo', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal '*/*', body + + get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' } + assert !ok? + + get '/stream', {}, { 'HTTP_ACCEPT' => 'text/event-stream' } + assert ok? + assert_equal 'text/event-stream', body + + get '/stream', {}, { 'HTTP_ACCEPT' => '' } + assert ok? + assert_equal '', body + + get '/stream', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal '*/*', body + + get '/stream', {}, { 'HTTP_ACCEPT' => 'application/xml' } + assert !ok? + end + + it "filters by current Content-Type" do + mock_app do + before('/txt') { content_type :txt } + get('*', :provides => :txt) { 'txt' } + + before('/html') { content_type :html } + get('*', :provides => :html) { 'html' } + end + + get '/', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type'] + assert_body 'txt' + + get '/txt', {}, { 'HTTP_ACCEPT' => 'text/plain' } + assert ok? + assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type'] + assert_body 'txt' + + get '/', {}, { 'HTTP_ACCEPT' => 'text/html' } + assert ok? + assert_equal 'text/html;charset=utf-8', response.headers['Content-Type'] + assert_body 'html' + end + + it "doesn't allow provides of passed routes to interfere with provides of other routes" do + mock_app do + get('/:foo', :provides => :txt) do + pass if params[:foo] != 'foo' + + 'foo' + end + + get('/bar', :provides => :html) { 'bar' } + end + + get '/foo', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type'] + assert_body 'foo' + + get '/bar', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal 'text/html;charset=utf-8', response.headers['Content-Type'] + assert_body 'bar' + end + + it "allows multiple mime types for accept header" do + types = ['image/jpeg', 'image/pjpeg'] + + mock_app { + get '/', :provides => types do + env['HTTP_ACCEPT'] + end + } + + types.each do |type| + get '/', {}, { 'HTTP_ACCEPT' => type } + assert ok? + assert_equal type, body + assert_equal type, response.headers['Content-Type'] + end + end + + it 'respects user agent preferences for the content type' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,text/html;q=0.8' } + assert_body 'text/html;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.8,text/html;q=0.5' } + assert_body 'image/png' + end + + it 'accepts generic types' do + mock_app do + get('/', :provides => :xml) { content_type } + get('/') { 'no match' } + end + get '/', {}, { 'HTTP_ACCEPT' => 'foo/*' } + assert_body 'no match' + get '/', {}, { 'HTTP_ACCEPT' => 'application/*' } + assert_body 'application/xml;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => '*/*' } + assert_body 'application/xml;charset=utf-8' + end + + it 'prefers concrete over partly generic types' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'image/*, text/html' } + assert_body 'text/html;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png, text/*' } + assert_body 'image/png' + end + + it 'prefers concrete over fully generic types' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/html' } + assert_body 'text/html;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png, */*' } + assert_body 'image/png' + end + + it 'prefers partly generic over fully generic types' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/*' } + assert_body 'text/html;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'image/*, */*' } + assert_body 'image/png' + end + + it 'respects quality with generic types' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'image/*;q=1, text/html;q=0' } + assert_body 'image/png' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*;q=0.7' } + assert_body 'text/html;charset=utf-8' + end + + it 'supplies a default quality of 1.0' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*' } + assert_body 'text/html;charset=utf-8' + end + + it 'orders types with equal quality by parameter count' do + mock_app do + get('/', :provides => [:png, :jpg]) { content_type } + end + + lo_png = 'image/png;q=0.5' + hi_png = 'image/png;q=0.5;profile=FOGRA40;gamma=0.8' + jpeg = 'image/jpeg;q=0.5;compress=0.25' + + get '/', {}, { 'HTTP_ACCEPT' => "#{lo_png}, #{jpeg}" } + assert_body 'image/jpeg' + get '/', {}, { 'HTTP_ACCEPT' => "#{hi_png}, #{jpeg}" } + assert_body 'image/png' + end + + it 'ignores the quality parameter when ordering by parameter count' do + mock_app do + get('/', :provides => [:png, :jpg]) { content_type } + end + + lo_png = 'image/png' + hi_png = 'image/png;profile=FOGRA40;gamma=0.8' + jpeg = 'image/jpeg;q=1.0;compress=0.25' + + get '/', {}, { 'HTTP_ACCEPT' => "#{jpeg}, #{lo_png}" } + assert_body 'image/jpeg' + get '/', {}, { 'HTTP_ACCEPT' => "#{jpeg}, #{hi_png}" } + assert_body 'image/png' + end + + it 'properly handles quoted strings in parameters' do + mock_app do + get('/', :provides => [:png, :jpg]) { content_type } + end + + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5;profile=",image/jpeg,"' } + assert_body 'image/png' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,image/jpeg;q=0;x=";q=1.0"' } + assert_body 'image/png' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,image/jpeg;q=0;x="\";q=1.0"' } + assert_body 'image/png' + end + + it 'accepts both text/javascript and application/javascript for js' do + mock_app { get('/', :provides => :js) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'application/javascript' } + assert_body 'application/javascript;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'text/javascript' } + assert_body 'text/javascript;charset=utf-8' + end + + it 'accepts both text/xml and application/xml for xml' do + mock_app { get('/', :provides => :xml) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' } + assert_body 'application/xml;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'text/xml' } + assert_body 'text/xml;charset=utf-8' + end + + it 'matches content-type to mime_type' do + mime_type = 'application/rss+xml;version="http://purl.org/rss/1.0/"' + mock_app do + configure { mime_type(:rss10, mime_type) } + get('/', :provides => [:rss10]) { content_type } + end + + get '/', {}, { 'HTTP_ACCEPT' => 'application/rss+xml' } + assert ok? + assert_body mime_type + end + + it 'handles missing mime_types with 404' do + mock_app do + configure { mime_type(:rss10, 'application/rss+xml') } + get('/', :provides => [:jpg]) { content_type } + end + + get '/', {}, { 'HTTP_ACCEPT' => 'application/rss+xml' } + assert_equal 404, status + assert_equal 'text/html;charset=utf-8', response['Content-Type'] + end + + it 'passes a single url param as block parameters when one param is specified' do + mock_app { + get '/:foo' do |foo| + assert_equal 'bar', foo + end + } + + get '/bar' + assert ok? + end + + it 'passes multiple params as block parameters when many are specified' do + mock_app { + get '/:foo/:bar/:baz' do |foo, bar, baz| + assert_equal 'abc', foo + assert_equal 'def', bar + assert_equal 'ghi', baz + end + } + + get '/abc/def/ghi' + assert ok? + end + + it 'passes regular expression captures as block parameters' do + mock_app { + get(/\/fo(.*)\/ba(.*)/) do |foo, bar| + assert_equal 'orooomma', foo + assert_equal 'f', bar + 'looks good' + end + } + + get '/foorooomma/baf' + assert ok? + assert_equal 'looks good', body + end + + it "supports mixing multiple splat params like /*/foo/*/* as block parameters" do + mock_app { + get '/*/foo/*/*' do |foo, bar, baz| + assert_equal 'bar', foo + assert_equal 'bling', bar + assert_equal 'baz/boom', baz + 'looks good' + end + } + + get '/bar/foo/bling/baz/boom' + assert ok? + assert_equal 'looks good', body + end + + it "uses the default encoding for block parameters" do + mock_app { + set :default_encoding ,'ISO-8859-1' + + get '/:foo/:bar' do |foo, bar| + "foo=#{foo.encoding};bar=#{bar.encoding}" + end + } + get '/f%C3%B6%C3%B6/b%C3%B6%C3%B6' + assert ok? + assert_equal 'foo=ISO-8859-1;bar=ISO-8859-1', body + end + + it 'raises an ArgumentError with block arity > 1 and too many values' do + mock_app do + get '/:foo/:bar/:baz' do |foo, bar| + 'quux' + end + end + + assert_raises(ArgumentError) { get '/a/b/c' } + end + + it 'raises an ArgumentError with block param arity > 1 and too few values' do + mock_app { + get '/:foo/:bar' do |foo, bar, baz| + 'quux' + end + } + + assert_raises(ArgumentError) { get '/a/b' } + end + + it 'succeeds if no block parameters are specified' do + mock_app { + get '/:foo/:bar' do + 'quux' + end + } + + get '/a/b' + assert ok? + assert_equal 'quux', body + end + + it 'passes all params with block param arity -1 (splat args)' do + mock_app { + get '/:foo/:bar' do |*args| + args.join + end + } + + get '/a/b' + assert ok? + assert_equal 'ab', body + end + + it 'allows custom route-conditions to be set via route options' do + protector = Module.new { + def protect(*args) + condition { + unless authorize(params["user"], params["password"]) + halt 403, "go away" + end + } + end + } + + mock_app { + register protector + + helpers do + def authorize(username, password) + username == "foo" && password == "bar" + end + end + + get "/", :protect => true do + "hey" + end + } + + get "/" + assert forbidden? + assert_equal "go away", body + + get "/", :user => "foo", :password => "bar" + assert ok? + assert_equal "hey", body + end + + it 'raises an ArgumentError with block param arity 1 and no values' do + mock_app { + get '/foo' do |foo| + 'quux' + end + } + + assert_raises(ArgumentError) { get '/foo' } + end + + it 'raises an ArgumentError with block param arity 1 and too many values' do + mock_app { + get '/:foo/:bar/:baz' do |foo| + 'quux' + end + } + + assert_raises(ArgumentError) { get '/a/b/c' } + end + + it "matches routes defined in superclasses" do + base = Class.new(Sinatra::Base) + base.get('/foo') { 'foo in baseclass' } + + mock_app(base) { + get('/bar') { 'bar in subclass' } + } + + get '/foo' + assert ok? + assert_equal 'foo in baseclass', body + + get '/bar' + assert ok? + assert_equal 'bar in subclass', body + end + + it "matches routes in subclasses before superclasses" do + base = Class.new(Sinatra::Base) + base.get('/foo') { 'foo in baseclass' } + base.get('/bar') { 'bar in baseclass' } + + mock_app(base) { + get('/foo') { 'foo in subclass' } + } + + get '/foo' + assert ok? + assert_equal 'foo in subclass', body + + get '/bar' + assert ok? + assert_equal 'bar in baseclass', body + end + + it "adds hostname condition when it is in options" do + mock_app { + get '/foo', :host => 'host' do + 'foo' + end + } + + get '/foo' + assert not_found? + end + + it 'allows using call to fire another request internally' do + mock_app do + get '/foo' do + status, headers, body = call env.merge("PATH_INFO" => '/bar') + [status, headers, body.each.map(&:upcase)] + end + + get '/bar' do + "bar" + end + end + + get '/foo' + assert ok? + assert_body "BAR" + end + + it 'plays well with other routing middleware' do + middleware = Sinatra.new + inner_app = Sinatra.new { get('/foo') { 'hello' } } + builder = Rack::Builder.new do + use middleware + map('/test') { run inner_app } + end + + @app = builder.to_app + get '/test/foo' + assert ok? + assert_body 'hello' + end + + it 'returns the route signature' do + signature = list = nil + + mock_app do + signature = post('/') { } + list = routes['POST'] + end + + assert_equal Array, signature.class + assert_equal 3, signature.length + assert list.include?(signature) + end + + it "sets env['sinatra.route'] to the matched route" do + mock_app do + after do + assert_equal 'GET /users/:id/status', env['sinatra.route'] + end + get('/users/:id/status') { 'ok' } + end + get '/users/1/status' + end + + it 'treats routes with and without trailing slashes differently' do + mock_app do + get '/foo' do + 'Foo' + end + + get '/foo/' do + 'Foo with a slash' + end + end + + get '/foo' + assert_equal 'Foo', body + refute_equal 'Foo with a slash', body + + get '/foo/' + assert_equal 'Foo with a slash', body + end + + it 'does not treat routes with and without trailing slashes differently if :strict_paths is disabled' do + mock_app do + disable :strict_paths + + get '/foo' do + 'foo' + end + end + + get '/foo' + assert_equal 'foo', body + + get '/foo/' + assert_equal 'foo', body + end +end diff --git a/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/old.rb b/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/old.rb new file mode 100644 index 0000000..96007e4 --- /dev/null +++ b/tests/testdata/ruby_sinatra_add_regression_test_for_conten_2/old.rb @@ -0,0 +1,1702 @@ +require_relative 'test_helper' + +# Helper method for easy route pattern matching testing +def route_def(pattern) + mock_app { get(pattern) { } } +end + +class PatternLookAlike + def to_pattern(*) + self + end + + def params(input) + { "one" => +"this", "two" => +"is", "three" => +"a", "four" => +"test" } + end +end + +class RoutingTest < Minitest::Test + %w[get put post delete options patch link unlink].each do |verb| + it "defines #{verb.upcase} request handlers with #{verb}" do + mock_app { + send verb, '/hello' do + 'Hello World' + end + } + + request = Rack::MockRequest.new(@app) + response = request.request(verb.upcase, '/hello', {}) + assert response.ok? + assert_equal 'Hello World', response.body + end + end + + it "defines HEAD request handlers with HEAD" do + mock_app { + head '/hello' do + response['X-Hello'] = 'World!' + 'remove me' + end + } + + request = Rack::MockRequest.new(@app) + response = request.request('HEAD', '/hello', {}) + assert response.ok? + assert_equal 'World!', response['X-Hello'] + assert_equal '', response.body + end + + it "400s when request params contain conflicting types" do + mock_app { + get('/foo') { } + } + + request = Rack::MockRequest.new(@app) + response = request.request('GET', '/foo?bar=&bar[]=', {}) + assert response.bad_request? + end + + it "404s when no route satisfies the request" do + mock_app { + get('/foo') { } + } + get '/bar' + assert_equal 404, status + end + + it "404s and sets X-Cascade header when no route satisfies the request" do + mock_app { + get('/foo') { } + } + get '/bar' + assert_equal 404, status + assert_equal 'pass', response.headers['X-Cascade'] + end + + it "404s and does not set X-Cascade header when no route satisfies the request and x_cascade has been disabled" do + mock_app { + disable :x_cascade + get('/foo') { } + } + get '/bar' + assert_equal 404, status + assert_nil response.headers['X-Cascade'] + end + + + it "allows using unicode" do + mock_app do + get('/föö') { } + end + get '/f%C3%B6%C3%B6' + assert_equal 200, status + end + + it "it handles encoded slashes correctly" do + mock_app { + set :protection, :except => :path_traversal + get("/:a") { |a| a } + } + get '/foo%2Fbar' + assert_equal 200, status + assert_body "foo/bar" + end + + it "it handles encoded colons correctly" do + mock_app { + get("/\\:") { 'a' } + get("/a/\\:") { 'b' } + get("/a/\\:/b") { 'c' } + get("/a/b\\:") { 'd' } + get("/a/b\\: ") { 'e' } + } + get '/:' + assert_equal 200, status + assert_body "a" + get '/%3a' + assert_equal 200, status + assert_body "a" + + get '/a/:' + assert_equal 200, status + assert_body "b" + get '/a/%3a' + assert_equal 200, status + assert_body "b" + + get '/a/:/b' + assert_equal 200, status + assert_body "c" + get '/a/%3A/b' + assert_equal 200, status + assert_body "c" + + get '/a/b:' + assert_equal 200, status + assert_body "d" + get '/a/b%3a' + assert_equal 200, status + assert_body "d" + + get '/a/b%3a%20' + assert_equal 200, status + assert_body "e" + get '/a/b%3a+' + assert_equal 200, status + assert_body "e" + end + + it "overrides the content-type in error handlers" do + mock_app { + before { content_type 'text/plain' } + error Sinatra::NotFound do + content_type "text/html" + "

Not Found

" + end + } + + get '/foo' + assert_equal 404, status + assert_equal 'text/html;charset=utf-8', response["Content-Type"] + assert_equal "

Not Found

", response.body + end + + it "recalculates body length correctly for 404 response" do + mock_app { + get '/' do + @response["Content-Length"] = "30" + raise Sinatra::NotFound + end + } + + get "/" + assert_equal "18", response["Content-Length"] + assert_equal 404, status + end + + it "captures the exception message of a raised NotFound" do + mock_app { + get '/' do + raise Sinatra::NotFound, "This is not a drill" + end + } + + get "/" + assert_equal "19", response["Content-Length"] + assert_equal 404, status + assert_equal "This is not a drill", response.body + end + + it "captures the exception message of a raised BadRequest" do + mock_app { + get '/' do + raise Sinatra::BadRequest, "This is not a drill either" + end + } + + get "/" + assert_equal "26", response["Content-Length"] + assert_equal 400, status + assert_equal "This is not a drill either", response.body + end + + it "captures the custom exception message of a BadRequest" do + mock_app { + get('/') {} + + error Sinatra::BadRequest do + 'This is not a drill either' + end + } + + get "/", "foo" => "", "foo[]" => "" + assert_equal "26", response["Content-Length"] + assert_equal 400, status + assert_equal "This is not a drill either", response.body + end + + it "returns empty when unmatched with any regex captures" do + mock_app do + before do + # noop + end + + get '/hello' do + params.to_s + end + end + + assert get('/hello').ok? + assert_body '{}' + end + + it "uses 404 error handler for not matching route" do + mock_app { + not_found do + "nf" + end + error 404 do + "e" + end + } + + get "/" + assert_equal "e", body + assert_equal 404, status + end + + it 'matches empty PATH_INFO to "/" if no route is defined for ""' do + mock_app do + get '/' do + 'worked' + end + end + + get '/', {}, "PATH_INFO" => "" + assert ok? + assert_equal 'worked', body + rescue Rack::Lint::LintError => error + # Temporary fix for https://github.com/sinatra/sinatra/issues/2113 + skip error.message + end + + it 'matches empty PATH_INFO to "" if a route is defined for ""' do + mock_app do + disable :protection + + get '/' do + 'did not work' + end + + get '' do + 'worked' + end + end + + get '/', {}, "PATH_INFO" => "" + assert ok? + assert_equal 'worked', body + rescue Rack::Lint::LintError => error + # Temporary fix for https://github.com/sinatra/sinatra/issues/2113 + skip error.message + end + + it 'takes multiple definitions of a route' do + mock_app { + user_agent(/Foo/) + get '/foo' do + 'foo' + end + + get '/foo' do + 'not foo' + end + } + + get '/foo', {}, 'HTTP_USER_AGENT' => 'Foo' + assert ok? + assert_equal 'foo', body + + get '/foo' + assert ok? + assert_equal 'not foo', body + end + + it "exposes params with indifferent hash" do + mock_app { + get '/:foo' do + assert_equal 'bar', params['foo'] + assert params.has_key?('foo') + assert_equal 'bar', params[:foo] + assert params.has_key?(:foo) + 'well, alright' + end + } + get '/bar' + assert_equal 'well, alright', body + end + + it "handles params without a value" do + mock_app { + get '/' do + assert_nil params.fetch('foo') + "Given: #{params.keys.sort.join(',')}" + end + } + get '/?foo' + assert_equal 'Given: foo', body + end + + it "merges named params and query string params in params" do + mock_app { + get '/:foo' do + assert_equal 'bar', params['foo'] + assert_equal 'biz', params['baz'] + end + } + get '/bar?baz=biz' + assert ok? + end + + it "supports named params like /hello/:person" do + mock_app { + get '/hello/:person' do + "Hello #{params['person']}" + end + } + get '/hello/Frank' + assert_equal 'Hello Frank', body + end + + it "supports optional named params like /?:foo?/?:bar?" do + mock_app { + get '/?:foo?/?:bar?' do + "foo=#{params[:foo]};bar=#{params[:bar]}" + end + } + + get '/hello/world' + assert ok? + assert_equal "foo=hello;bar=world", body + + get '/hello' + assert ok? + assert_equal "foo=hello;bar=", body + + get '/hello?bar=baz' + assert ok? + assert_equal "foo=hello;bar=baz", body + + get '/' + assert ok? + assert_equal "foo=;bar=", body + end + + it "uses the default encoding for named params" do + mock_app { + set :default_encoding ,'ISO-8859-1' + + get '/:foo/:bar' do + "foo=#{params[:foo].encoding};bar=#{params[:bar].encoding}" + end + } + get '/f%C3%B6%C3%B6/b%C3%B6%C3%B6' + assert ok? + assert_equal 'foo=ISO-8859-1;bar=ISO-8859-1', body + end + + it "supports named captures like %r{/hello/(?[^/?#]+)}" do + mock_app { + get Regexp.new('/hello/(?[^/?#]+)') do + "Hello #{params['person']}" + end + } + get '/hello/Frank' + assert_equal 'Hello Frank', body + end + + it "supports optional named captures like %r{/page(?.[^/?#]+)?}" do + mock_app { + get Regexp.new('/page(?.[^/?#]+)?') do + "format=#{params[:format]}" + end + } + + get '/page.html' + assert ok? + assert_equal "format=.html", body + + get '/page.xml' + assert ok? + assert_equal "format=.xml", body + + get '/page' + assert ok? + assert_equal "format=", body + end + + it 'uses the default encoding for named captures' do + mock_app { + set :default_encoding ,'ISO-8859-1' + + get Regexp.new('/page(?.[^/?#]+)?') do + "format=#{params[:format].encoding};captures=#{params[:captures][0].encoding}" + end + } + get '/page.f%C3%B6' + assert ok? + assert_equal 'format=ISO-8859-1;captures=ISO-8859-1', body + end + + it 'does not concatenate params with the same name' do + mock_app { get('/:foo') { params[:foo] } } + get '/a?foo=b' + assert_body 'a' + end + + it "supports single splat params like /*" do + mock_app { + get '/*' do + assert params['splat'].kind_of?(Array) + params['splat'].join "\n" + end + } + + get '/foo' + assert_equal "foo", body + + get '/foo/bar/baz' + assert_equal "foo/bar/baz", body + end + + it "supports mixing multiple splat params like /*/foo/*/*" do + mock_app { + get '/*/foo/*/*' do + assert params['splat'].kind_of?(Array) + params['splat'].join "\n" + end + } + + get '/bar/foo/bling/baz/boom' + assert_equal "bar\nbling\nbaz/boom", body + + get '/bar/foo/baz' + assert not_found? + end + + it "supports mixing named and splat params like /:foo/*" do + mock_app { + get '/:foo/*' do + assert_equal 'foo', params['foo'] + assert_equal ['bar/baz'], params['splat'] + end + } + + get '/foo/bar/baz' + assert ok? + end + + it "matches a dot ('.') as part of a named param" do + mock_app { + get '/:foo/:bar' do + params[:foo] + end + } + + get '/user@example.com/name' + assert_equal 200, response.status + assert_equal 'user@example.com', body + end + + it "matches a literal dot ('.') outside of named params" do + mock_app { + get '/:file.:ext' do + assert_equal 'pony', params[:file] + assert_equal 'jpg', params[:ext] + 'right on' + end + } + + get '/pony.jpg' + assert_equal 200, response.status + assert_equal 'right on', body + end + + it "literally matches dot in paths" do + route_def '/test.bar' + + get '/test.bar' + assert ok? + get 'test0bar' + assert not_found? + end + + it "literally matches dollar sign in paths" do + route_def '/test$/' + + get '/test$/' + assert ok? + end + + it "literally matches plus sign in paths" do + route_def '/te+st/' + + get '/te%2Bst/' + assert ok? + get '/teeeeeeest/' + assert not_found? + end + + it "does not convert plus sign into space as the value of a named param" do + mock_app do + get '/:test' do + params["test"] + end + end + get '/bob+ross' + assert ok? + assert_equal 'bob+ross', body + end + + it "literally matches parens in paths when escaped" do + route_def '/test\(bar\)/' + + get '/test(bar)/' + assert ok? + end + + it "supports basic nested params" do + mock_app { + get '/hi' do + params["person"]["name"] + end + } + + get "/hi?person[name]=John+Doe" + assert ok? + assert_equal "John Doe", body + end + + it "exposes nested params with indifferent hash" do + mock_app { + get '/testme' do + assert_equal 'baz', params['bar']['foo'] + assert_equal 'baz', params['bar'][:foo] + 'well, alright' + end + } + get '/testme?bar[foo]=baz' + assert_equal 'well, alright', body + end + + it "exposes params nested within arrays with indifferent hash" do + mock_app { + get '/testme' do + assert_equal 'baz', params['bar'][0]['foo'] + assert_equal 'baz', params['bar'][0][:foo] + 'well, alright' + end + } + get '/testme?bar[][foo]=baz' + assert_equal 'well, alright', body + end + + it "supports arrays within params" do + mock_app { + get '/foo' do + assert_equal ['A', 'B'], params['bar'] + 'looks good' + end + } + get '/foo?bar[]=A&bar[]=B' + assert ok? + assert_equal 'looks good', body + end + + it "supports deeply nested params" do + expected_params = { + "emacs" => { + "map" => { "goto-line" => "M-g g" }, + "version" => "22.3.1" + }, + "browser" => { + "firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}}, + "chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}} + }, + "paste" => {"name"=>"hello world", "syntax"=>"ruby"} + } + mock_app { + get '/foo' do + assert_equal expected_params, params + 'looks good' + end + } + get '/foo', expected_params + assert ok? + assert_equal 'looks good', body + end + + it "preserves non-nested params" do + mock_app { + get '/foo' do + assert_equal "2", params["article_id"] + assert_equal "awesome", params['comment']['body'] + assert_nil params['comment[body]'] + 'looks good' + end + } + + get '/foo?article_id=2&comment[body]=awesome' + assert ok? + assert_equal 'looks good', body + end + + it "matches paths that include spaces encoded with %20" do + mock_app { + get '/path with spaces' do + 'looks good' + end + } + + get '/path%20with%20spaces' + assert ok? + assert_equal 'looks good', body + end + + it "matches paths that include spaces encoded with +" do + mock_app { + get '/path with spaces' do + 'looks good' + end + } + + get '/path+with+spaces' + assert ok? + assert_equal 'looks good', body + end + + it "matches paths that include ampersands" do + mock_app { + get '/:name' do + 'looks good' + end + } + + get '/foo&bar' + assert ok? + assert_equal 'looks good', body + end + + it "URL decodes named parameters and splats" do + mock_app { + get '/:foo/*' do + assert_equal 'hello world', params['foo'] + assert_equal ['how are you'], params['splat'] + nil + end + } + + get '/hello%20world/how%20are%20you' + assert ok? + end + + it 'unescapes named parameters and splats' do + mock_app { + get '/:foo/*' do |a, b| + assert_equal "foo\xE2\x80\x8Cbar", params['foo'] + assert_predicate params['foo'], :valid_encoding? + + assert_equal ["bar\xE2\x80\x8Cbaz"], params['splat'] + end + } + + get '/foo%e2%80%8cbar/bar%e2%80%8cbaz' + assert ok? + end + + it 'supports regular expressions' do + mock_app { + get(/\/foo...\/bar/) do + 'Hello World' + end + } + + get '/foooom/bar' + assert ok? + assert_equal 'Hello World', body + end + + it 'unescapes regular expression captures' do + mock_app { + get(/\/foo\/(.+)/) do |path| + path + end + } + + get '/foo/bar%e2%80%8cbaz' + assert ok? + assert_equal "bar\xE2\x80\x8Cbaz", body + assert_predicate body, :valid_encoding? + end + + it 'makes regular expression captures available in params[:captures]' do + mock_app { + get(/\/fo(.*)\/ba(.*)/) do + assert_equal ['orooomma', 'f'], params[:captures] + 'right on' + end + } + + get '/foorooomma/baf' + assert ok? + assert_equal 'right on', body + end + + it 'makes regular expression captures available in params[:captures] for concatenated routes' do + with_regexp = Mustermann.new('/prefix') + Mustermann.new("/fo(.*)/ba(.*)", type: :regexp) + without_regexp = Mustermann.new('/prefix', type: :identity) + Mustermann.new('/baz') + mock_app { + get(with_regexp) do + assert_equal ['orooomma', 'f'], params[:captures] + 'right on' + end + get(without_regexp) do + assert !params.keys.include?(:captures) + 'no captures here' + end + } + + get '/prefix/foorooomma/baf' + assert ok? + assert_equal 'right on', body + + get '/prefix/baz' + assert ok? + assert_equal 'no captures here', body + end + + it 'supports regular expression look-alike routes' do + mock_app { + get(PatternLookAlike.new) do + assert_equal 'this', params[:one] + assert_equal 'is', params[:two] + assert_equal 'a', params[:three] + assert_equal 'test', params[:four] + 'right on' + end + } + + get '/this/is/a/test/' + assert ok? + assert_equal 'right on', body + end + + it 'raises a TypeError when pattern is not a String or Regexp' do + assert_raises(TypeError) { + mock_app { get(42){} } + } + end + + it "returns response immediately on halt" do + mock_app { + get '/' do + halt 'Hello World' + 'Boo-hoo World' + end + } + + get '/' + assert ok? + assert_equal 'Hello World', body + end + + it "halts with a response tuple" do + mock_app { + get '/' do + halt 295, {'Content-Type' => 'text/plain'}, 'Hello World' + end + } + + get '/' + assert_equal 295, status + assert_equal 'text/plain', response['Content-Type'] + assert_equal 'Hello World', body + end + + it "halts with an array of strings" do + mock_app { + get '/' do + halt %w[Hello World How Are You] + end + } + + get '/' + assert_equal 'HelloWorldHowAreYou', body + end + + it 'sets response.status with halt' do + status_was = nil + mock_app do + after { status_was = status } + get('/') { halt 500, 'error' } + end + get '/' + assert_status 500 + assert_equal 500, status_was + end + + it "transitions to the next matching route on pass" do + mock_app { + get '/:foo' do + pass + 'Hello Foo' + end + + get '/*' do + assert !params.include?('foo') + 'Hello World' + end + } + + get '/bar' + assert ok? + assert_equal 'Hello World', body + end + + it "makes original request params available in error handler" do + mock_app { + disable :raise_errors + + get '/:foo' do + raise ArgumentError, "foo" + end + + error do + "Hello #{params['foo']}2" + end + } + + get '/bar' + assert_equal 'Hello bar2', body + end + + it "transitions to 404 when passed and no subsequent route matches" do + mock_app { + get '/:foo' do + pass + 'Hello Foo' + end + } + + get '/bar' + assert not_found? + end + + it "transitions to 404 and sets X-Cascade header when passed and no subsequent route matches" do + mock_app { + get '/:foo' do + pass + 'Hello Foo' + end + + get '/bar' do + 'Hello Bar' + end + } + + get '/foo' + assert not_found? + assert_equal 'pass', response.headers['X-Cascade'] + end + + it "uses optional block passed to pass as route block if no other route is found" do + mock_app { + get "/" do + pass do + "this" + end + "not this" + end + } + + get "/" + assert ok? + assert "this", body + end + + it "uses optional block passed to pass as route block if no other route is found and superclass has non-matching routes" do + base = Class.new(Sinatra::Base) + base.get('/foo') { 'foo in baseclass' } + + mock_app(base) { + get "/" do + pass do + "this" + end + "not this" + end + } + + get "/" + assert_equal 200, status + assert "this", body + end + + it "passes when matching condition returns false" do + mock_app { + condition { params[:foo] == 'bar' } + get '/:foo' do + 'Hello World' + end + } + + get '/bar' + assert ok? + assert_equal 'Hello World', body + + get '/foo' + assert not_found? + end + + it "does not pass when matching condition returns nil" do + mock_app { + condition { nil } + get '/:foo' do + 'Hello World' + end + } + + get '/bar' + assert ok? + assert_equal 'Hello World', body + end + + it "passes to next route when condition calls pass explicitly" do + mock_app { + condition { pass unless params[:foo] == 'bar' } + get '/:foo' do + 'Hello World' + end + } + + get '/bar' + assert ok? + assert_equal 'Hello World', body + + get '/foo' + assert not_found? + end + + it "passes to the next route when host_name does not match" do + mock_app { + host_name 'example.com' + get '/foo' do + 'Hello World' + end + } + get '/foo' + assert not_found? + + get '/foo', {}, { 'HTTP_HOST' => 'example.com' } + assert_equal 200, status + assert_equal 'Hello World', body + end + + it "passes to the next route when user_agent does not match" do + mock_app { + user_agent(/Foo/) + get '/foo' do + 'Hello World' + end + } + get '/foo' + assert not_found? + + get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' } + assert_equal 200, status + assert_equal 'Hello World', body + end + + it "treats missing user agent like an empty string" do + mock_app do + user_agent(/.*/) + get '/' do + "Hello World" + end + end + get '/' + assert_equal 200, status + assert_equal 'Hello World', body + end + + it "makes captures in user agent pattern available in params[:agent]" do + mock_app { + user_agent(/Foo (.*)/) + get '/foo' do + 'Hello ' + params[:agent].first + end + } + get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' } + assert_equal 200, status + assert_equal 'Hello Bar', body + end + + it 'matches mime_types with dots, hyphens and plus signs' do + mime_types = %w( + application/atom+xml + application/ecmascript + application/EDI-X12 + application/EDIFACT + application/json + application/javascript + application/octet-stream + application/ogg + application/pdf + application/postscript + application/rdf+xml + application/rss+xml + application/soap+xml + application/font-woff + application/xhtml+xml + application/xml + application/xml-dtd + application/xop+xml + application/zip + application/gzip + audio/basic + audio/L24 + audio/mp4 + audio/mpeg + audio/ogg + audio/vorbis + audio/vnd.rn-realaudio + audio/vnd.wave + audio/webm + image/gif + image/jpeg + image/pjpeg + image/png + image/svg+xml + image/tiff + image/vnd.microsoft.icon + message/http + message/imdn+xml + message/partial + message/rfc822 + model/example + model/iges + model/mesh + model/vrml + model/x3d+binary + model/x3d+vrml + model/x3d+xml + multipart/mixed + multipart/alternative + multipart/related + multipart/form-data + multipart/signed + multipart/encrypted + text/cmd + text/css + text/csv + text/html + text/javascript + application/javascript + text/plain + text/vcard + text/xml + video/mpeg + video/mp4 + video/ogg + video/quicktime + video/webm + video/x-matroska + video/x-ms-wmv + video/x-flv + application/vnd.oasis.opendocument.text + application/vnd.oasis.opendocument.spreadsheet + application/vnd.oasis.opendocument.presentation + application/vnd.oasis.opendocument.graphics + application/vnd.ms-excel + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + application/vnd.ms-powerpoint + application/vnd.openxmlformats-officedocument.presentationml.presentation + application/vnd.openxmlformats-officedocument.wordprocessingml.document + application/vnd.mozilla.xul+xml + application/vnd.google-earth.kml+xml + application/x-deb + application/x-dvi + application/x-font-ttf + application/x-javascript + application/x-latex + application/x-mpegURL + application/x-rar-compressed + application/x-shockwave-flash + application/x-stuffit + application/x-tar + application/x-www-form-urlencoded + application/x-xpinstall + audio/x-aac + audio/x-caf + image/x-xcf + text/x-gwt-rpc + text/x-jquery-tmpl + application/x-pkcs12 + application/x-pkcs12 + application/x-pkcs7-certificates + application/x-pkcs7-certificates + application/x-pkcs7-certreqresp + application/x-pkcs7-mime + application/x-pkcs7-mime + application/x-pkcs7-signature + ) + + mime_types.each { |mime_type| assert mime_type.match(Sinatra::Request::HEADER_VALUE_WITH_PARAMS) } + end + + it "filters by accept header" do + mock_app { + get '/', :provides => :xml do + env['HTTP_ACCEPT'] + end + get '/foo', :provides => :html do + env['HTTP_ACCEPT'] + end + get '/stream', :provides => 'text/event-stream' do + env['HTTP_ACCEPT'] + end + } + + get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' } + assert ok? + assert_equal 'application/xml', body + assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type'] + + get '/', {}, {} + assert ok? + assert_equal '', body + assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type'] + + get '/', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal '*/*', body + assert_equal 'application/xml;charset=utf-8', response.headers['Content-Type'] + + get '/', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' } + assert !ok? + + get '/foo', {}, { 'HTTP_ACCEPT' => 'text/html;q=0.9' } + assert ok? + assert_equal 'text/html;q=0.9', body + + get '/foo', {}, { 'HTTP_ACCEPT' => '' } + assert ok? + assert_equal '', body + + get '/foo', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal '*/*', body + + get '/foo', {}, { 'HTTP_ACCEPT' => 'application/xml' } + assert !ok? + + get '/stream', {}, { 'HTTP_ACCEPT' => 'text/event-stream' } + assert ok? + assert_equal 'text/event-stream', body + + get '/stream', {}, { 'HTTP_ACCEPT' => '' } + assert ok? + assert_equal '', body + + get '/stream', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal '*/*', body + + get '/stream', {}, { 'HTTP_ACCEPT' => 'application/xml' } + assert !ok? + end + + it "filters by current Content-Type" do + mock_app do + before('/txt') { content_type :txt } + get('*', :provides => :txt) { 'txt' } + + before('/html') { content_type :html } + get('*', :provides => :html) { 'html' } + end + + get '/', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type'] + assert_body 'txt' + + get '/txt', {}, { 'HTTP_ACCEPT' => 'text/plain' } + assert ok? + assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type'] + assert_body 'txt' + + get '/', {}, { 'HTTP_ACCEPT' => 'text/html' } + assert ok? + assert_equal 'text/html;charset=utf-8', response.headers['Content-Type'] + assert_body 'html' + end + + it "doesn't allow provides of passed routes to interfere with provides of other routes" do + mock_app do + get('/:foo', :provides => :txt) do + pass if params[:foo] != 'foo' + + 'foo' + end + + get('/bar', :provides => :html) { 'bar' } + end + + get '/foo', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type'] + assert_body 'foo' + + get '/bar', {}, { 'HTTP_ACCEPT' => '*/*' } + assert ok? + assert_equal 'text/html;charset=utf-8', response.headers['Content-Type'] + assert_body 'bar' + end + + it "allows multiple mime types for accept header" do + types = ['image/jpeg', 'image/pjpeg'] + + mock_app { + get '/', :provides => types do + env['HTTP_ACCEPT'] + end + } + + types.each do |type| + get '/', {}, { 'HTTP_ACCEPT' => type } + assert ok? + assert_equal type, body + assert_equal type, response.headers['Content-Type'] + end + end + + it 'respects user agent preferences for the content type' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,text/html;q=0.8' } + assert_body 'text/html;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.8,text/html;q=0.5' } + assert_body 'image/png' + end + + it 'accepts generic types' do + mock_app do + get('/', :provides => :xml) { content_type } + get('/') { 'no match' } + end + get '/', {}, { 'HTTP_ACCEPT' => 'foo/*' } + assert_body 'no match' + get '/', {}, { 'HTTP_ACCEPT' => 'application/*' } + assert_body 'application/xml;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => '*/*' } + assert_body 'application/xml;charset=utf-8' + end + + it 'prefers concrete over partly generic types' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'image/*, text/html' } + assert_body 'text/html;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png, text/*' } + assert_body 'image/png' + end + + it 'prefers concrete over fully generic types' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/html' } + assert_body 'text/html;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png, */*' } + assert_body 'image/png' + end + + it 'prefers partly generic over fully generic types' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => '*/*, text/*' } + assert_body 'text/html;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'image/*, */*' } + assert_body 'image/png' + end + + it 'respects quality with generic types' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'image/*;q=1, text/html;q=0' } + assert_body 'image/png' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*;q=0.7' } + assert_body 'text/html;charset=utf-8' + end + + it 'supplies a default quality of 1.0' do + mock_app { get('/', :provides => [:png, :html]) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*' } + assert_body 'text/html;charset=utf-8' + end + + it 'orders types with equal quality by parameter count' do + mock_app do + get('/', :provides => [:png, :jpg]) { content_type } + end + + lo_png = 'image/png;q=0.5' + hi_png = 'image/png;q=0.5;profile=FOGRA40;gamma=0.8' + jpeg = 'image/jpeg;q=0.5;compress=0.25' + + get '/', {}, { 'HTTP_ACCEPT' => "#{lo_png}, #{jpeg}" } + assert_body 'image/jpeg' + get '/', {}, { 'HTTP_ACCEPT' => "#{hi_png}, #{jpeg}" } + assert_body 'image/png' + end + + it 'ignores the quality parameter when ordering by parameter count' do + mock_app do + get('/', :provides => [:png, :jpg]) { content_type } + end + + lo_png = 'image/png' + hi_png = 'image/png;profile=FOGRA40;gamma=0.8' + jpeg = 'image/jpeg;q=1.0;compress=0.25' + + get '/', {}, { 'HTTP_ACCEPT' => "#{jpeg}, #{lo_png}" } + assert_body 'image/jpeg' + get '/', {}, { 'HTTP_ACCEPT' => "#{jpeg}, #{hi_png}" } + assert_body 'image/png' + end + + it 'properly handles quoted strings in parameters' do + mock_app do + get('/', :provides => [:png, :jpg]) { content_type } + end + + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5;profile=",image/jpeg,"' } + assert_body 'image/png' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,image/jpeg;q=0;x=";q=1.0"' } + assert_body 'image/png' + get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,image/jpeg;q=0;x="\";q=1.0"' } + assert_body 'image/png' + end + + it 'accepts both text/javascript and application/javascript for js' do + mock_app { get('/', :provides => :js) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'application/javascript' } + assert_body 'application/javascript;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'text/javascript' } + assert_body 'text/javascript;charset=utf-8' + end + + it 'accepts both text/xml and application/xml for xml' do + mock_app { get('/', :provides => :xml) { content_type }} + get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' } + assert_body 'application/xml;charset=utf-8' + get '/', {}, { 'HTTP_ACCEPT' => 'text/xml' } + assert_body 'text/xml;charset=utf-8' + end + + it 'matches content-type to mime_type' do + mime_type = 'application/rss+xml;version="http://purl.org/rss/1.0/"' + mock_app do + configure { mime_type(:rss10, mime_type) } + get('/', :provides => [:rss10]) { content_type } + end + + get '/', {}, { 'HTTP_ACCEPT' => 'application/rss+xml' } + assert ok? + assert_body mime_type + end + + it 'handles missing mime_types with 404' do + mock_app do + configure { mime_type(:rss10, 'application/rss+xml') } + get('/', :provides => [:jpg]) { content_type } + end + + get '/', {}, { 'HTTP_ACCEPT' => 'application/rss+xml' } + assert_equal 404, status + assert_equal 'text/html;charset=utf-8', response['Content-Type'] + end + + it 'passes a single url param as block parameters when one param is specified' do + mock_app { + get '/:foo' do |foo| + assert_equal 'bar', foo + end + } + + get '/bar' + assert ok? + end + + it 'passes multiple params as block parameters when many are specified' do + mock_app { + get '/:foo/:bar/:baz' do |foo, bar, baz| + assert_equal 'abc', foo + assert_equal 'def', bar + assert_equal 'ghi', baz + end + } + + get '/abc/def/ghi' + assert ok? + end + + it 'passes regular expression captures as block parameters' do + mock_app { + get(/\/fo(.*)\/ba(.*)/) do |foo, bar| + assert_equal 'orooomma', foo + assert_equal 'f', bar + 'looks good' + end + } + + get '/foorooomma/baf' + assert ok? + assert_equal 'looks good', body + end + + it "supports mixing multiple splat params like /*/foo/*/* as block parameters" do + mock_app { + get '/*/foo/*/*' do |foo, bar, baz| + assert_equal 'bar', foo + assert_equal 'bling', bar + assert_equal 'baz/boom', baz + 'looks good' + end + } + + get '/bar/foo/bling/baz/boom' + assert ok? + assert_equal 'looks good', body + end + + it "uses the default encoding for block parameters" do + mock_app { + set :default_encoding ,'ISO-8859-1' + + get '/:foo/:bar' do |foo, bar| + "foo=#{foo.encoding};bar=#{bar.encoding}" + end + } + get '/f%C3%B6%C3%B6/b%C3%B6%C3%B6' + assert ok? + assert_equal 'foo=ISO-8859-1;bar=ISO-8859-1', body + end + + it 'raises an ArgumentError with block arity > 1 and too many values' do + mock_app do + get '/:foo/:bar/:baz' do |foo, bar| + 'quux' + end + end + + assert_raises(ArgumentError) { get '/a/b/c' } + end + + it 'raises an ArgumentError with block param arity > 1 and too few values' do + mock_app { + get '/:foo/:bar' do |foo, bar, baz| + 'quux' + end + } + + assert_raises(ArgumentError) { get '/a/b' } + end + + it 'succeeds if no block parameters are specified' do + mock_app { + get '/:foo/:bar' do + 'quux' + end + } + + get '/a/b' + assert ok? + assert_equal 'quux', body + end + + it 'passes all params with block param arity -1 (splat args)' do + mock_app { + get '/:foo/:bar' do |*args| + args.join + end + } + + get '/a/b' + assert ok? + assert_equal 'ab', body + end + + it 'allows custom route-conditions to be set via route options' do + protector = Module.new { + def protect(*args) + condition { + unless authorize(params["user"], params["password"]) + halt 403, "go away" + end + } + end + } + + mock_app { + register protector + + helpers do + def authorize(username, password) + username == "foo" && password == "bar" + end + end + + get "/", :protect => true do + "hey" + end + } + + get "/" + assert forbidden? + assert_equal "go away", body + + get "/", :user => "foo", :password => "bar" + assert ok? + assert_equal "hey", body + end + + it 'raises an ArgumentError with block param arity 1 and no values' do + mock_app { + get '/foo' do |foo| + 'quux' + end + } + + assert_raises(ArgumentError) { get '/foo' } + end + + it 'raises an ArgumentError with block param arity 1 and too many values' do + mock_app { + get '/:foo/:bar/:baz' do |foo| + 'quux' + end + } + + assert_raises(ArgumentError) { get '/a/b/c' } + end + + it "matches routes defined in superclasses" do + base = Class.new(Sinatra::Base) + base.get('/foo') { 'foo in baseclass' } + + mock_app(base) { + get('/bar') { 'bar in subclass' } + } + + get '/foo' + assert ok? + assert_equal 'foo in baseclass', body + + get '/bar' + assert ok? + assert_equal 'bar in subclass', body + end + + it "matches routes in subclasses before superclasses" do + base = Class.new(Sinatra::Base) + base.get('/foo') { 'foo in baseclass' } + base.get('/bar') { 'bar in baseclass' } + + mock_app(base) { + get('/foo') { 'foo in subclass' } + } + + get '/foo' + assert ok? + assert_equal 'foo in subclass', body + + get '/bar' + assert ok? + assert_equal 'bar in baseclass', body + end + + it "adds hostname condition when it is in options" do + mock_app { + get '/foo', :host => 'host' do + 'foo' + end + } + + get '/foo' + assert not_found? + end + + it 'allows using call to fire another request internally' do + mock_app do + get '/foo' do + status, headers, body = call env.merge("PATH_INFO" => '/bar') + [status, headers, body.each.map(&:upcase)] + end + + get '/bar' do + "bar" + end + end + + get '/foo' + assert ok? + assert_body "BAR" + end + + it 'plays well with other routing middleware' do + middleware = Sinatra.new + inner_app = Sinatra.new { get('/foo') { 'hello' } } + builder = Rack::Builder.new do + use middleware + map('/test') { run inner_app } + end + + @app = builder.to_app + get '/test/foo' + assert ok? + assert_body 'hello' + end + + it 'returns the route signature' do + signature = list = nil + + mock_app do + signature = post('/') { } + list = routes['POST'] + end + + assert_equal Array, signature.class + assert_equal 3, signature.length + assert list.include?(signature) + end + + it "sets env['sinatra.route'] to the matched route" do + mock_app do + after do + assert_equal 'GET /users/:id/status', env['sinatra.route'] + end + get('/users/:id/status') { 'ok' } + end + get '/users/1/status' + end + + it 'treats routes with and without trailing slashes differently' do + mock_app do + get '/foo' do + 'Foo' + end + + get '/foo/' do + 'Foo with a slash' + end + end + + get '/foo' + assert_equal 'Foo', body + refute_equal 'Foo with a slash', body + + get '/foo/' + assert_equal 'Foo with a slash', body + end + + it 'does not treat routes with and without trailing slashes differently if :strict_paths is disabled' do + mock_app do + disable :strict_paths + + get '/foo' do + 'foo' + end + end + + get '/foo' + assert_equal 'foo', body + + get '/foo/' + assert_equal 'foo', body + end +end diff --git a/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/expected.json b/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/expected.json new file mode 100644 index 0000000..d24c610 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/expected.json @@ -0,0 +1,238 @@ +{ + "version": "v1", + "actions": [ + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 3, + 11 + ], + "type": "comment", + "label": "# register Sinatra::HamlHelpers", + "start_byte": 398, + "end_byte": 433 + }, + "old_value": "# register Sinatra::HamlHelpers", + "new_value": "# helpers Sinatra::HamlHelpers", + "dest_node": { + "tree": "after", + "path": [ + 3, + 11 + ], + "type": "comment", + "label": "# helpers Sinatra::HamlHelpers", + "start_byte": 398, + "end_byte": 432 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/metadata.json b/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/metadata.json new file mode 100644 index 0000000..0d35ade --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "f2fe3e3a1b9f9ab10a3f3adcf842de7d49b554eb", + "commit": "ef00c6a0db0ef31143e529e7e6edddf2d5c99653", + "message": "Fix `Sinatra::HamlHelpers` docs (#2046)\n\n`helpers` is the one we want to use here:", + "author": "Patrik Ragnarsson", + "date": "2024-10-21T00:51:08+02:00", + "language": "ruby", + "files_changed": 1, + "insertions": 1, + "deletions": 1 +} diff --git a/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/new.rb b/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/new.rb new file mode 100644 index 0000000..74bf0e4 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/new.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'sinatra/base' +require 'sinatra/capture' + +module Sinatra + # = Sinatra::HamlHelpers + # + # This extension provides some of the helper methods that existed in Haml 5 + # but were removed in Haml 6. To use this in your app, just +register+ it: + # + # require 'sinatra/base' + # require 'sinatra/haml_helpers' + # + # class Application < Sinatra::Base + # helpers Sinatra::HamlHelpers + # + # # now you can use the helpers in your views + # get '/' do + # haml_code = <<~HAML + # %p + # != surround "(", ")" do + # %a{ href: "https://example.org/" } example.org + # HAML + # haml haml_code + # end + # end + # + module HamlHelpers + include Sinatra::Capture + + def surround(front, back = front, &block) + "#{front}#{_capture_haml(&block).chomp}#{back}\n" + end + + def precede(str, &block) + "#{str}#{_capture_haml(&block).chomp}\n" + end + + def succeed(str, &block) + "#{_capture_haml(&block).chomp}#{str}\n" + end + + def _capture_haml(*args, &block) + capture(*args, &block) + end + end + + helpers HamlHelpers +end diff --git a/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/old.rb b/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/old.rb new file mode 100644 index 0000000..0aff3fc --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix__sinatra__hamlhelpers__doc_1/old.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'sinatra/base' +require 'sinatra/capture' + +module Sinatra + # = Sinatra::HamlHelpers + # + # This extension provides some of the helper methods that existed in Haml 5 + # but were removed in Haml 6. To use this in your app, just +register+ it: + # + # require 'sinatra/base' + # require 'sinatra/haml_helpers' + # + # class Application < Sinatra::Base + # register Sinatra::HamlHelpers + # + # # now you can use the helpers in your views + # get '/' do + # haml_code = <<~HAML + # %p + # != surround "(", ")" do + # %a{ href: "https://example.org/" } example.org + # HAML + # haml haml_code + # end + # end + # + module HamlHelpers + include Sinatra::Capture + + def surround(front, back = front, &block) + "#{front}#{_capture_haml(&block).chomp}#{back}\n" + end + + def precede(str, &block) + "#{str}#{_capture_haml(&block).chomp}\n" + end + + def succeed(str, &block) + "#{_capture_haml(&block).chomp}#{str}\n" + end + + def _capture_haml(*args, &block) + capture(*args, &block) + end + end + + helpers HamlHelpers +end diff --git a/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/expected.json b/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/expected.json new file mode 100644 index 0000000..9fa4b79 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/expected.json @@ -0,0 +1,2776 @@ +{ + "version": "v1", + "actions": [ + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 34, + 2, + 1, + 2, + 1, + 0 + ], + "type": "string", + "label": "'includes falcon on mruby'", + "start_byte": 12440, + "end_byte": 12466 + }, + "old_value": "'includes falcon on mruby'", + "new_value": "'includes falcon on non-jruby'", + "dest_node": { + "tree": "after", + "path": [ + 1, + 3, + 34, + 2, + 1, + 2, + 1, + 0 + ], + "type": "string", + "label": "'includes falcon on non-jruby'", + "start_byte": 12440, + "end_byte": 12470 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 34, + 2, + 1, + 2, + 2, + 1, + 0, + 1, + 1 + ], + "type": "comparison_operator_literal", + "label": "==", + "start_byte": 12491, + "end_byte": 12493 + } + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 3, + 34, + 2, + 1, + 2, + 2, + 1, + 0, + 1, + 1 + ], + "type": "comparison_operator_literal", + "label": "!=", + "start_byte": 12495, + "end_byte": 12497 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 3, + 34, + 2, + 1, + 2, + 2, + 1, + 0, + 1 + ], + "type": "binary", + "start_byte": 12483, + "end_byte": 12505 + }, + "position": 1 + }, + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 34, + 2, + 1, + 2, + 2, + 1, + 0, + 1, + 2 + ], + "type": "string", + "label": "'ruby'", + "start_byte": 12494, + "end_byte": 12500 + }, + "old_value": "'ruby'", + "new_value": "'jruby'", + "dest_node": { + "tree": "after", + "path": [ + 1, + 3, + 34, + 2, + 1, + 2, + 2, + 1, + 0, + 1, + 2 + ], + "type": "string", + "label": "'jruby'", + "start_byte": 12498, + "end_byte": 12505 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": 277, + "right_line": 277 + }, + { + "left_line": 278, + "right_line": 278 + }, + { + "left_line": 279, + "right_line": 279 + }, + { + "left_line": 280, + "right_line": 280 + }, + { + "left_line": 281, + "right_line": 281 + }, + { + "left_line": 282, + "right_line": 282 + }, + { + "left_line": 283, + "right_line": 283 + }, + { + "left_line": 284, + "right_line": 284 + }, + { + "left_line": 285, + "right_line": 285 + }, + { + "left_line": 286, + "right_line": 286 + }, + { + "left_line": 287, + "right_line": 287 + }, + { + "left_line": 288, + "right_line": 288 + }, + { + "left_line": 289, + "right_line": 289 + }, + { + "left_line": 290, + "right_line": 290 + }, + { + "left_line": 291, + "right_line": 291 + }, + { + "left_line": 292, + "right_line": 292 + }, + { + "left_line": 293, + "right_line": 293 + }, + { + "left_line": 294, + "right_line": 294 + }, + { + "left_line": 295, + "right_line": 295 + }, + { + "left_line": 296, + "right_line": 296 + }, + { + "left_line": 297, + "right_line": 297 + }, + { + "left_line": 298, + "right_line": 298 + }, + { + "left_line": 299, + "right_line": 299 + }, + { + "left_line": 300, + "right_line": 300 + }, + { + "left_line": 301, + "right_line": 301 + }, + { + "left_line": 302, + "right_line": 302 + }, + { + "left_line": 303, + "right_line": 303 + }, + { + "left_line": 304, + "right_line": 304 + }, + { + "left_line": 305, + "right_line": 305 + }, + { + "left_line": 306, + "right_line": 306 + }, + { + "left_line": 307, + "right_line": 307 + }, + { + "left_line": 308, + "right_line": 308 + }, + { + "left_line": 309, + "right_line": 309 + }, + { + "left_line": 310, + "right_line": 310 + }, + { + "left_line": 311, + "right_line": 311 + }, + { + "left_line": 312, + "right_line": 312 + }, + { + "left_line": 313, + "right_line": 313 + }, + { + "left_line": 314, + "right_line": 314 + }, + { + "left_line": 315, + "right_line": 315 + }, + { + "left_line": 316, + "right_line": 316 + }, + { + "left_line": 317, + "right_line": 317 + }, + { + "left_line": 318, + "right_line": 318 + }, + { + "left_line": 319, + "right_line": 319 + }, + { + "left_line": 320, + "right_line": 320 + }, + { + "left_line": 321, + "right_line": 321 + }, + { + "left_line": 322, + "right_line": 322 + }, + { + "left_line": 323, + "right_line": 323 + }, + { + "left_line": 324, + "right_line": 324 + }, + { + "left_line": 325, + "right_line": 325 + }, + { + "left_line": 326, + "right_line": 326 + }, + { + "left_line": 327, + "right_line": 327 + }, + { + "left_line": 328, + "right_line": 328 + }, + { + "left_line": 329, + "right_line": 329 + }, + { + "left_line": 330, + "right_line": 330 + }, + { + "left_line": 331, + "right_line": 331 + }, + { + "left_line": 332, + "right_line": 332 + }, + { + "left_line": 333, + "right_line": 333 + }, + { + "left_line": 334, + "right_line": 334 + }, + { + "left_line": 335, + "right_line": 335 + }, + { + "left_line": 336, + "right_line": 336 + }, + { + "left_line": 337, + "right_line": 337 + }, + { + "left_line": 338, + "right_line": 338 + }, + { + "left_line": 339, + "right_line": 339 + }, + { + "left_line": 340, + "right_line": 340 + }, + { + "left_line": 341, + "right_line": 341 + }, + { + "left_line": 342, + "right_line": 342 + }, + { + "left_line": 343, + "right_line": 343 + }, + { + "left_line": 344, + "right_line": 344 + }, + { + "left_line": 345, + "right_line": 345 + }, + { + "left_line": 346, + "right_line": 346 + }, + { + "left_line": 347, + "right_line": 347 + }, + { + "left_line": 348, + "right_line": 348 + }, + { + "left_line": 349, + "right_line": 349 + }, + { + "left_line": 350, + "right_line": 350 + }, + { + "left_line": 351, + "right_line": 351 + }, + { + "left_line": 352, + "right_line": 352 + }, + { + "left_line": 353, + "right_line": 353 + }, + { + "left_line": 354, + "right_line": 354 + }, + { + "left_line": 355, + "right_line": 355 + }, + { + "left_line": 356, + "right_line": 356 + }, + { + "left_line": 357, + "right_line": 357 + }, + { + "left_line": 358, + "right_line": 358 + }, + { + "left_line": 359, + "right_line": 359 + }, + { + "left_line": 360, + "right_line": 360 + }, + { + "left_line": 361, + "right_line": 361 + }, + { + "left_line": 362, + "right_line": 362 + }, + { + "left_line": 363, + "right_line": 363 + }, + { + "left_line": 364, + "right_line": 364 + }, + { + "left_line": 365, + "right_line": 365 + }, + { + "left_line": 366, + "right_line": 366 + }, + { + "left_line": 367, + "right_line": 367 + }, + { + "left_line": 368, + "right_line": 368 + }, + { + "left_line": 369, + "right_line": 369 + }, + { + "left_line": 370, + "right_line": 370 + }, + { + "left_line": 371, + "right_line": 371 + }, + { + "left_line": 372, + "right_line": 372 + }, + { + "left_line": 373, + "right_line": 373 + }, + { + "left_line": 374, + "right_line": 374 + }, + { + "left_line": 375, + "right_line": 375 + }, + { + "left_line": 376, + "right_line": 376 + }, + { + "left_line": 377, + "right_line": 377 + }, + { + "left_line": 378, + "right_line": 378 + }, + { + "left_line": 379, + "right_line": 379 + }, + { + "left_line": 380, + "right_line": 380 + }, + { + "left_line": 381, + "right_line": 381 + }, + { + "left_line": 382, + "right_line": 382 + }, + { + "left_line": 383, + "right_line": 383 + }, + { + "left_line": 384, + "right_line": 384 + }, + { + "left_line": 385, + "right_line": 385 + }, + { + "left_line": 386, + "right_line": 386 + }, + { + "left_line": 387, + "right_line": 387 + }, + { + "left_line": 388, + "right_line": 388 + }, + { + "left_line": 389, + "right_line": 389 + }, + { + "left_line": 390, + "right_line": 390 + }, + { + "left_line": 391, + "right_line": 391 + }, + { + "left_line": 392, + "right_line": 392 + }, + { + "left_line": 393, + "right_line": 393 + }, + { + "left_line": 394, + "right_line": 394 + }, + { + "left_line": 395, + "right_line": 395 + }, + { + "left_line": 396, + "right_line": 396 + }, + { + "left_line": 397, + "right_line": 397 + }, + { + "left_line": 398, + "right_line": 398 + }, + { + "left_line": 399, + "right_line": 399 + }, + { + "left_line": 400, + "right_line": 400 + }, + { + "left_line": 401, + "right_line": 401 + }, + { + "left_line": 402, + "right_line": 402 + }, + { + "left_line": 403, + "right_line": 403 + }, + { + "left_line": 404, + "right_line": 404 + }, + { + "left_line": 405, + "right_line": 405 + }, + { + "left_line": 406, + "right_line": 406 + }, + { + "left_line": 407, + "right_line": 407 + }, + { + "left_line": 408, + "right_line": 408 + }, + { + "left_line": 409, + "right_line": 409 + }, + { + "left_line": 410, + "right_line": 410 + }, + { + "left_line": 411, + "right_line": 411 + }, + { + "left_line": 412, + "right_line": 412 + }, + { + "left_line": 413, + "right_line": 413 + }, + { + "left_line": 414, + "right_line": 414 + }, + { + "left_line": 415, + "right_line": 415 + }, + { + "left_line": 416, + "right_line": 416 + }, + { + "left_line": 417, + "right_line": 417 + }, + { + "left_line": 418, + "right_line": 418 + }, + { + "left_line": 419, + "right_line": 419 + }, + { + "left_line": 420, + "right_line": 420 + }, + { + "left_line": 421, + "right_line": 421 + }, + { + "left_line": 422, + "right_line": 422 + }, + { + "left_line": 423, + "right_line": 423 + }, + { + "left_line": 424, + "right_line": 424 + }, + { + "left_line": 425, + "right_line": 425 + }, + { + "left_line": 426, + "right_line": 426 + }, + { + "left_line": 427, + "right_line": 427 + }, + { + "left_line": 428, + "right_line": 428 + }, + { + "left_line": 429, + "right_line": 429 + }, + { + "left_line": 430, + "right_line": 430 + }, + { + "left_line": 431, + "right_line": 431 + }, + { + "left_line": 432, + "right_line": 432 + }, + { + "left_line": 433, + "right_line": 433 + }, + { + "left_line": 434, + "right_line": 434 + }, + { + "left_line": 435, + "right_line": 435 + }, + { + "left_line": 436, + "right_line": 436 + }, + { + "left_line": 437, + "right_line": 437 + }, + { + "left_line": 438, + "right_line": 438 + }, + { + "left_line": 439, + "right_line": 439 + }, + { + "left_line": 440, + "right_line": 440 + }, + { + "left_line": 441, + "right_line": 441 + }, + { + "left_line": 442, + "right_line": 442 + }, + { + "left_line": 443, + "right_line": 443 + }, + { + "left_line": 444, + "right_line": 444 + }, + { + "left_line": 445, + "right_line": 445 + }, + { + "left_line": 446, + "right_line": 446 + }, + { + "left_line": 447, + "right_line": 447 + }, + { + "left_line": 448, + "right_line": 448 + }, + { + "left_line": 449, + "right_line": 449 + }, + { + "left_line": 450, + "right_line": 450 + }, + { + "left_line": 451, + "right_line": 451 + }, + { + "left_line": 452, + "right_line": 452 + }, + { + "left_line": 453, + "right_line": 453 + }, + { + "left_line": 454, + "right_line": 454 + }, + { + "left_line": 455, + "right_line": 455 + }, + { + "left_line": 456, + "right_line": 456 + }, + { + "left_line": 457, + "right_line": 457 + }, + { + "left_line": 458, + "right_line": 458 + }, + { + "left_line": 459, + "right_line": 459 + }, + { + "left_line": 460, + "right_line": 460 + }, + { + "left_line": 461, + "right_line": 461 + }, + { + "left_line": 462, + "right_line": 462 + }, + { + "left_line": 463, + "right_line": 463 + }, + { + "left_line": 464, + "right_line": 464 + }, + { + "left_line": 465, + "right_line": 465 + }, + { + "left_line": 466, + "right_line": 466 + }, + { + "left_line": 467, + "right_line": 467 + }, + { + "left_line": 468, + "right_line": 468 + }, + { + "left_line": 469, + "right_line": 469 + }, + { + "left_line": 470, + "right_line": 470 + }, + { + "left_line": 471, + "right_line": 471 + }, + { + "left_line": 472, + "right_line": 472 + }, + { + "left_line": 473, + "right_line": 473 + }, + { + "left_line": 474, + "right_line": 474 + }, + { + "left_line": 475, + "right_line": 475 + }, + { + "left_line": 476, + "right_line": 476 + }, + { + "left_line": 477, + "right_line": 477 + }, + { + "left_line": 478, + "right_line": 478 + }, + { + "left_line": 479, + "right_line": 479 + }, + { + "left_line": 480, + "right_line": 480 + }, + { + "left_line": 481, + "right_line": 481 + }, + { + "left_line": 482, + "right_line": 482 + }, + { + "left_line": 483, + "right_line": 483 + }, + { + "left_line": 484, + "right_line": 484 + }, + { + "left_line": 485, + "right_line": 485 + }, + { + "left_line": 486, + "right_line": 486 + }, + { + "left_line": 487, + "right_line": 487 + }, + { + "left_line": 488, + "right_line": 488 + }, + { + "left_line": 489, + "right_line": 489 + }, + { + "left_line": 490, + "right_line": 490 + }, + { + "left_line": 491, + "right_line": 491 + }, + { + "left_line": 492, + "right_line": 492 + }, + { + "left_line": 493, + "right_line": 493 + }, + { + "left_line": 494, + "right_line": 494 + }, + { + "left_line": 495, + "right_line": 495 + }, + { + "left_line": 496, + "right_line": 496 + }, + { + "left_line": 497, + "right_line": 497 + }, + { + "left_line": 498, + "right_line": 498 + }, + { + "left_line": 499, + "right_line": 499 + }, + { + "left_line": 500, + "right_line": 500 + }, + { + "left_line": 501, + "right_line": 501 + }, + { + "left_line": 502, + "right_line": 502 + }, + { + "left_line": 503, + "right_line": 503 + }, + { + "left_line": 504, + "right_line": 504 + }, + { + "left_line": 505, + "right_line": 505 + }, + { + "left_line": 506, + "right_line": 506 + }, + { + "left_line": 507, + "right_line": 507 + }, + { + "left_line": 508, + "right_line": 508 + }, + { + "left_line": 509, + "right_line": 509 + }, + { + "left_line": 510, + "right_line": 510 + }, + { + "left_line": 511, + "right_line": 511 + }, + { + "left_line": 512, + "right_line": 512 + }, + { + "left_line": 513, + "right_line": 513 + }, + { + "left_line": 514, + "right_line": 514 + }, + { + "left_line": 515, + "right_line": 515 + }, + { + "left_line": 516, + "right_line": 516 + }, + { + "left_line": 517, + "right_line": 517 + }, + { + "left_line": 518, + "right_line": 518 + }, + { + "left_line": 519, + "right_line": 519 + }, + { + "left_line": 520, + "right_line": 520 + }, + { + "left_line": 521, + "right_line": 521 + }, + { + "left_line": 522, + "right_line": 522 + }, + { + "left_line": 523, + "right_line": 523 + }, + { + "left_line": 524, + "right_line": 524 + }, + { + "left_line": 525, + "right_line": 525 + }, + { + "left_line": 526, + "right_line": 526 + }, + { + "left_line": 527, + "right_line": 527 + }, + { + "left_line": 528, + "right_line": 528 + }, + { + "left_line": 529, + "right_line": 529 + }, + { + "left_line": 530, + "right_line": 530 + }, + { + "left_line": 531, + "right_line": 531 + }, + { + "left_line": 532, + "right_line": 532 + }, + { + "left_line": 533, + "right_line": 533 + }, + { + "left_line": 534, + "right_line": 534 + }, + { + "left_line": 535, + "right_line": 535 + }, + { + "left_line": 536, + "right_line": 536 + }, + { + "left_line": 537, + "right_line": 537 + }, + { + "left_line": 538, + "right_line": 538 + }, + { + "left_line": 539, + "right_line": 539 + }, + { + "left_line": 540, + "right_line": 540 + }, + { + "left_line": 541, + "right_line": 541 + }, + { + "left_line": 542, + "right_line": 542 + }, + { + "left_line": 543, + "right_line": 543 + }, + { + "left_line": 544, + "right_line": 544 + }, + { + "left_line": 545, + "right_line": 545 + }, + { + "left_line": 546, + "right_line": 546 + }, + { + "left_line": 547, + "right_line": 547 + }, + { + "left_line": 548, + "right_line": 548 + }, + { + "left_line": 549, + "right_line": 549 + }, + { + "left_line": 550, + "right_line": 550 + }, + { + "left_line": 551, + "right_line": 551 + }, + { + "left_line": 552, + "right_line": 552 + }, + { + "left_line": 553, + "right_line": 553 + }, + { + "left_line": 554, + "right_line": 554 + }, + { + "left_line": 555, + "right_line": 555 + }, + { + "left_line": 556, + "right_line": 556 + }, + { + "left_line": 557, + "right_line": 557 + }, + { + "left_line": 558, + "right_line": 558 + }, + { + "left_line": 559, + "right_line": 559 + }, + { + "left_line": 560, + "right_line": 560 + }, + { + "left_line": 561, + "right_line": 561 + }, + { + "left_line": 562, + "right_line": 562 + }, + { + "left_line": 563, + "right_line": 563 + }, + { + "left_line": 564, + "right_line": 564 + }, + { + "left_line": 565, + "right_line": 565 + }, + { + "left_line": 566, + "right_line": 566 + }, + { + "left_line": 567, + "right_line": 567 + }, + { + "left_line": 568, + "right_line": 568 + }, + { + "left_line": 569, + "right_line": 569 + }, + { + "left_line": 570, + "right_line": 570 + }, + { + "left_line": 571, + "right_line": 571 + }, + { + "left_line": 572, + "right_line": 572 + }, + { + "left_line": 573, + "right_line": 573 + }, + { + "left_line": 574, + "right_line": 574 + }, + { + "left_line": 575, + "right_line": 575 + }, + { + "left_line": 576, + "right_line": 576 + }, + { + "left_line": 577, + "right_line": 577 + }, + { + "left_line": 578, + "right_line": 578 + }, + { + "left_line": 579, + "right_line": 579 + }, + { + "left_line": 580, + "right_line": 580 + }, + { + "left_line": 581, + "right_line": 581 + }, + { + "left_line": 582, + "right_line": 582 + }, + { + "left_line": 583, + "right_line": 583 + }, + { + "left_line": 584, + "right_line": 584 + }, + { + "left_line": 585, + "right_line": 585 + }, + { + "left_line": 586, + "right_line": 586 + }, + { + "left_line": 587, + "right_line": 587 + }, + { + "left_line": 588, + "right_line": 588 + }, + { + "left_line": 589, + "right_line": 589 + }, + { + "left_line": 590, + "right_line": 590 + }, + { + "left_line": 591, + "right_line": 591 + }, + { + "left_line": 592, + "right_line": 592 + }, + { + "left_line": 593, + "right_line": 593 + }, + { + "left_line": 594, + "right_line": 594 + }, + { + "left_line": 595, + "right_line": 595 + }, + { + "left_line": 596, + "right_line": 596 + }, + { + "left_line": 597, + "right_line": 597 + }, + { + "left_line": 598, + "right_line": 598 + }, + { + "left_line": 599, + "right_line": 599 + }, + { + "left_line": 600, + "right_line": 600 + }, + { + "left_line": 601, + "right_line": 601 + }, + { + "left_line": 602, + "right_line": 602 + }, + { + "left_line": 603, + "right_line": 603 + }, + { + "left_line": 604, + "right_line": 604 + }, + { + "left_line": 605, + "right_line": 605 + }, + { + "left_line": 606, + "right_line": 606 + }, + { + "left_line": 607, + "right_line": 607 + }, + { + "left_line": 608, + "right_line": 608 + }, + { + "left_line": 609, + "right_line": 609 + }, + { + "left_line": 610, + "right_line": 610 + }, + { + "left_line": 611, + "right_line": 611 + }, + { + "left_line": 612, + "right_line": 612 + }, + { + "left_line": 613, + "right_line": 613 + }, + { + "left_line": 614, + "right_line": 614 + }, + { + "left_line": 615, + "right_line": 615 + }, + { + "left_line": 616, + "right_line": 616 + }, + { + "left_line": 617, + "right_line": 617 + }, + { + "left_line": 618, + "right_line": 618 + }, + { + "left_line": 619, + "right_line": 619 + }, + { + "left_line": 620, + "right_line": 620 + }, + { + "left_line": 621, + "right_line": 621 + }, + { + "left_line": 622, + "right_line": 622 + }, + { + "left_line": 623, + "right_line": 623 + }, + { + "left_line": 624, + "right_line": 624 + }, + { + "left_line": 625, + "right_line": 625 + }, + { + "left_line": 626, + "right_line": 626 + }, + { + "left_line": 627, + "right_line": 627 + }, + { + "left_line": 628, + "right_line": 628 + }, + { + "left_line": 629, + "right_line": 629 + }, + { + "left_line": 630, + "right_line": 630 + }, + { + "left_line": 631, + "right_line": 631 + }, + { + "left_line": 632, + "right_line": 632 + }, + { + "left_line": 633, + "right_line": 633 + }, + { + "left_line": 634, + "right_line": 634 + }, + { + "left_line": 635, + "right_line": 635 + }, + { + "left_line": 636, + "right_line": 636 + }, + { + "left_line": 637, + "right_line": 637 + }, + { + "left_line": 638, + "right_line": 638 + }, + { + "left_line": 639, + "right_line": 639 + }, + { + "left_line": 640, + "right_line": 640 + }, + { + "left_line": 641, + "right_line": 641 + }, + { + "left_line": 642, + "right_line": 642 + }, + { + "left_line": 643, + "right_line": 643 + }, + { + "left_line": 644, + "right_line": 644 + }, + { + "left_line": 645, + "right_line": 645 + }, + { + "left_line": 646, + "right_line": 646 + }, + { + "left_line": 647, + "right_line": 647 + }, + { + "left_line": 648, + "right_line": 648 + }, + { + "left_line": 649, + "right_line": 649 + }, + { + "left_line": 650, + "right_line": 650 + }, + { + "left_line": 651, + "right_line": 651 + }, + { + "left_line": 652, + "right_line": 652 + }, + { + "left_line": 653, + "right_line": 653 + }, + { + "left_line": 654, + "right_line": 654 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/metadata.json b/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/metadata.json new file mode 100644 index 0000000..d4b2330 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "df1633efb26fa71177686dc1add8b37e0e0d6b6b", + "commit": "f1ee0a3a6782ced94d1587fda233ba2adc08f426", + "message": "Fix check for whether falcon is included\n\n* This is the same check as in lib/sinatra/base.rb", + "author": "Benoit Daloze", + "date": "2022-07-20T12:18:47+02:00", + "language": "ruby", + "files_changed": 1, + "insertions": 2, + "deletions": 2 +} diff --git a/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/new.rb b/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/new.rb new file mode 100644 index 0000000..5177098 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/new.rb @@ -0,0 +1,654 @@ +require File.expand_path('helper', __dir__) + +class SettingsTest < Minitest::Test + setup do + @base = Sinatra.new(Sinatra::Base) + @base.set :environment => :foo, :app_file => nil + + @application = Sinatra.new(Sinatra::Application) + @application.set :environment => :foo, :app_file => nil + end + + it 'sets settings to literal values' do + @base.set(:foo, 'bar') + assert @base.respond_to?(:foo) + assert_equal 'bar', @base.foo + end + + it 'sets settings to Procs' do + @base.set(:foo, Proc.new { 'baz' }) + assert @base.respond_to?(:foo) + assert_equal 'baz', @base.foo + end + + it 'sets settings using a block' do + @base.set(:foo){ 'baz' } + assert @base.respond_to?(:foo) + assert_equal 'baz', @base.foo + end + + it 'raises an error with a value and a block' do + assert_raises ArgumentError do + @base.set(:fiz, 'boom!'){ 'baz' } + end + assert !@base.respond_to?(:fiz) + end + + it 'raises an error without value and block' do + assert_raises(ArgumentError) { @base.set(:fiz) } + assert !@base.respond_to?(:fiz) + end + + it 'allows setting a value to the app class' do + @base.set :base, @base + assert @base.respond_to?(:base) + assert_equal @base, @base.base + end + + it 'raises an error with the app class as value and a block' do + assert_raises ArgumentError do + @base.set(:fiz, @base) { 'baz' } + end + assert !@base.respond_to?(:fiz) + end + + it "sets multiple settings with a Hash" do + @base.set :foo => 1234, + :bar => 'Hello World', + :baz => Proc.new { 'bizzle' } + assert_equal 1234, @base.foo + assert_equal 'Hello World', @base.bar + assert_equal 'bizzle', @base.baz + end + + it 'sets multiple settings using #each' do + @base.set [["foo", "bar"]] + assert_equal "bar", @base.foo + end + + it 'inherits settings methods when subclassed' do + @base.set :foo, 'bar' + @base.set :biz, Proc.new { 'baz' } + + sub = Class.new(@base) + assert sub.respond_to?(:foo) + assert_equal 'bar', sub.foo + assert sub.respond_to?(:biz) + assert_equal 'baz', sub.biz + end + + it 'overrides settings in subclass' do + @base.set :foo, 'bar' + @base.set :biz, Proc.new { 'baz' } + sub = Class.new(@base) + sub.set :foo, 'bling' + assert_equal 'bling', sub.foo + assert_equal 'bar', @base.foo + end + + it 'creates setter methods when first defined' do + @base.set :foo, 'bar' + assert @base.respond_to?('foo=') + @base.foo = 'biz' + assert_equal 'biz', @base.foo + end + + it 'creates predicate methods when first defined' do + @base.set :foo, 'hello world' + assert @base.respond_to?(:foo?) + assert @base.foo? + @base.set :foo, nil + assert !@base.foo? + end + + it 'uses existing setter methods if detected' do + class << @base + def foo + @foo + end + def foo=(value) + @foo = 'oops' + end + end + + @base.set :foo, 'bam' + assert_equal 'oops', @base.foo + end + + it 'merges values of multiple set calls if those are hashes' do + @base.set :foo, :a => 1 + sub = Class.new(@base) + sub.set :foo, :b => 2 + assert_equal({:a => 1, :b => 2}, sub.foo) + end + + it 'merging does not affect the superclass' do + @base.set :foo, :a => 1 + sub = Class.new(@base) + sub.set :foo, :b => 2 + assert_equal({:a => 1}, @base.foo) + end + + it 'is possible to change a value from a hash to something else' do + @base.set :foo, :a => 1 + @base.set :foo, :bar + assert_equal(:bar, @base.foo) + end + + it 'merges values with values of the superclass if those are hashes' do + @base.set :foo, :a => 1 + @base.set :foo, :b => 2 + assert_equal({:a => 1, :b => 2}, @base.foo) + end + + it "sets multiple settings to true with #enable" do + @base.enable :sessions, :foo, :bar + assert @base.sessions + assert @base.foo + assert @base.bar + end + + it "sets multiple settings to false with #disable" do + @base.disable :sessions, :foo, :bar + assert !@base.sessions + assert !@base.foo + assert !@base.bar + end + + it 'is accessible from instances via #settings' do + assert_equal :foo, @base.new!.settings.environment + end + + it 'is accessible from class via #settings' do + assert_equal :foo, @base.settings.environment + end + + describe 'default_content_type' do + it 'defaults to html' do + assert_equal 'text/html', @base.default_content_type + end + + it 'can be changed' do + @base.set :default_content_type, 'application/json' + @base.get('/') { '{"a":1}' } + @app = @base + get '/' + assert_equal 200, status + assert_equal 'application/json', response.content_type + end + + it 'can be disabled' do + @base.set :default_content_type, nil + @base.error(404) { "" } + @app = @base + get '/' + assert_equal 404, status + assert_nil response.content_type + assert_empty body + end + + it 'may emit content without a content-type (to be sniffed)' do + @base.set :default_content_type, nil + @base.get('/') { raise Sinatra::BadRequest, "This is a drill" } + @app = @base + get '/' + assert_equal 400, status + assert_nil response.content_type + assert_body "This is a drill" + end + end + + describe 'methodoverride' do + it 'is disabled on Base' do + assert ! @base.method_override? + end + + it 'is enabled on Application' do + assert @application.method_override? + end + + it 'enables MethodOverride middleware' do + @base.set :method_override, true + @base.put('/') { 'okay' } + @app = @base + post '/', {'_method'=>'PUT'}, {} + assert_equal 200, status + assert_equal 'okay', body + end + + it 'is backward compatible with methodoverride' do + assert ! @base.methodoverride? + @base.enable :methodoverride + assert @base.methodoverride? + end + + it 'ignores bundler/inline from callers' do + @application.stub(:caller, ->(_){ ['/path/to/bundler/inline.rb', $0] }) do + assert_equal File.expand_path($0), File.expand_path(@application.send(:caller_files).first) + end + end + end + + describe 'run' do + it 'is disabled on Base' do + assert ! @base.run? + end + + it 'is enabled on Application except in test environment' do + assert @application.run? + + @application.set :environment, :test + assert ! @application.run? + end + end + + describe 'raise_errors' do + it 'is enabled on Base only in test' do + assert ! @base.raise_errors? + + @base.set(:environment, :test) + assert @base.raise_errors? + end + + it 'is enabled on Application only in test' do + assert ! @application.raise_errors? + + @application.set(:environment, :test) + assert @application.raise_errors? + end + end + + describe 'show_exceptions' do + it 'is disabled on Base except under development' do + assert ! @base.show_exceptions? + @base.environment = :development + assert @base.show_exceptions? + end + + it 'is disabled on Application except in development' do + assert ! @application.show_exceptions? + + @application.set(:environment, :development) + assert @application.show_exceptions? + end + + it 'returns a friendly 500' do + klass = Sinatra.new(Sinatra::Application) + mock_app(klass) { + enable :show_exceptions + + get '/' do + raise StandardError + end + } + + get '/' + assert_equal 500, status + assert body.include?("StandardError") + assert body.include?("show_exceptions setting") + end + + it 'does not attempt to show unparseable query parameters' do + klass = Sinatra.new(Sinatra::Application) + mock_app(klass) { + enable :show_exceptions + + get '/' do + raise Sinatra::BadRequest + end + } + + get '/' + assert_equal 400, status + refute body.include?('
') + refute body.include?('
') + end + + it 'does not override app-specified error handling when set to :after_handler' do + ran = false + mock_app do + set :show_exceptions, :after_handler + error(RuntimeError) { ran = true } + get('/') { raise RuntimeError } + end + + get '/' + assert_equal 500, status + assert ran + end + + it 'does catch any other exceptions when set to :after_handler' do + ran = false + mock_app do + set :show_exceptions, :after_handler + error(RuntimeError) { ran = true } + get('/') { raise ArgumentError } + end + + get '/' + assert_equal 500, status + assert !ran + end + end + + describe 'dump_errors' do + it 'is disabled on Base in test' do + @base.environment = :test + assert ! @base.dump_errors? + @base.environment = :development + assert @base.dump_errors? + @base.environment = :production + assert @base.dump_errors? + end + + it 'dumps exception with backtrace to rack.errors' do + klass = Sinatra.new(Sinatra::Application) + + mock_app(klass) { + enable :dump_errors + disable :raise_errors + + error do + error = @env['rack.errors'].instance_variable_get(:@error) + error.rewind + + error.read + end + + get '/' do + raise + end + } + + get '/' + assert body.include?("RuntimeError") && body.include?("settings_test.rb") + end + + it 'does not dump 404 errors' do + klass = Sinatra.new(Sinatra::Application) + + mock_app(klass) { + enable :dump_errors + disable :raise_errors + + error do + error = @env['rack.errors'].instance_variable_get(:@error) + error.rewind + + error.read + end + + get '/' do + raise Sinatra::NotFound + end + } + + get '/' + assert !body.include?("NotFound") && !body.include?("settings_test.rb") + end + end + + describe 'sessions' do + it 'is disabled on Base' do + assert ! @base.sessions? + end + + it 'is disabled on Application' do + assert ! @application.sessions? + end + end + + describe 'logging' do + it 'is disabled on Base' do + assert ! @base.logging? + end + + it 'is enabled on Application except in test environment' do + assert @application.logging? + + @application.set :environment, :test + assert ! @application.logging + end + end + + describe 'static' do + it 'is disabled on Base by default' do + assert ! @base.static? + end + + it 'is enabled on Base when public_folder is set and exists' do + @base.set :environment, :development + @base.set :public_folder, __dir__ + assert @base.static? + end + + it 'is enabled on Base when root is set and root/public_folder exists' do + @base.set :environment, :development + @base.set :root, __dir__ + assert @base.static? + end + + it 'is disabled on Application by default' do + assert ! @application.static? + end + + it 'is enabled on Application when public_folder is set and exists' do + @application.set :environment, :development + @application.set :public_folder, __dir__ + assert @application.static? + end + + it 'is enabled on Application when root is set and root/public_folder exists' do + @application.set :environment, :development + @application.set :root, __dir__ + assert @application.static? + end + + it 'is possible to use Module#public' do + @base.send(:define_method, :foo) { } + @base.send(:private, :foo) + assert !@base.public_method_defined?(:foo) + @base.send(:public, :foo) + assert @base.public_method_defined?(:foo) + end + + it 'is possible to use the keyword public in a sinatra app' do + app = Sinatra.new do + private + def priv; end + public + def pub; end + end + assert !app.public_method_defined?(:priv) + assert app.public_method_defined?(:pub) + end + end + + describe 'bind' do + it 'defaults to 0.0.0.0' do + assert_equal '0.0.0.0', @base.bind + assert_equal '0.0.0.0', @application.bind + end + end + + describe 'port' do + it 'defaults to 4567' do + assert_equal 4567, @base.port + assert_equal 4567, @application.port + end + end + + describe 'server' do + it 'includes webrick' do + assert @base.server.include?('webrick') + assert @application.server.include?('webrick') + end + + it 'includes puma' do + assert @base.server.include?('puma') + assert @application.server.include?('puma') + end + + it 'includes falcon on non-jruby' do + if RUBY_ENGINE != 'jruby' + assert @base.server.include?('falcon') + assert @application.server.include?('falcon') + else + assert !@base.server.include?('falcon') + assert !@application.server.include?('falcon') + end + end + end + + describe 'app_file' do + it 'is nil for base classes' do + assert_nil Sinatra::Base.app_file + assert_nil Sinatra::Application.app_file + end + + it 'defaults to the file subclassing' do + assert_equal File.expand_path(__FILE__), Sinatra.new.app_file + end + end + + describe 'root' do + it 'is nil if app_file is not set' do + assert_nil @base.root + assert_nil @application.root + end + + it 'is equal to the expanded basename of app_file' do + @base.app_file = __FILE__ + assert_equal File.expand_path(__dir__), @base.root + + @application.app_file = __FILE__ + assert_equal File.expand_path(__dir__), @application.root + end + end + + describe 'views' do + it 'is nil if root is not set' do + assert_nil @base.views + assert_nil @application.views + end + + it 'is set to root joined with views/' do + @base.root = __dir__ + assert_equal __dir__ + "/views", @base.views + + @application.root = __dir__ + assert_equal __dir__ + "/views", @application.views + end + end + + describe 'public_folder' do + it 'is nil if root is not set' do + assert_nil @base.public_folder + assert_nil @application.public_folder + end + + it 'is set to root joined with public/' do + @base.root = __dir__ + assert_equal __dir__ + "/public", @base.public_folder + + @application.root = __dir__ + assert_equal __dir__ + "/public", @application.public_folder + end + end + + describe 'public_dir' do + it 'is an alias for public_folder' do + @base.public_dir = __dir__ + assert_equal __dir__, @base.public_dir + assert_equal @base.public_folder, @base.public_dir + + @application.public_dir = __dir__ + assert_equal __dir__, @application.public_dir + assert_equal @application.public_folder, @application.public_dir + end + end + + describe 'lock' do + it 'is disabled by default' do + assert ! @base.lock? + assert ! @application.lock? + end + end + + describe 'protection' do + class MiddlewareTracker < Rack::Builder + def self.track + Rack.send :remove_const, :Builder + Rack.const_set :Builder, MiddlewareTracker + MiddlewareTracker.used.clear + yield + ensure + Rack.send :remove_const, :Builder + Rack.const_set :Builder, MiddlewareTracker.superclass + end + + def self.used + @used ||= [] + end + + def use(middleware, *) + MiddlewareTracker.used << middleware + super + end + end + + it 'sets up Rack::Protection' do + MiddlewareTracker.track do + Sinatra::Base.new + assert_include MiddlewareTracker.used, Rack::Protection + end + end + + it 'sets up Rack::Protection::PathTraversal' do + MiddlewareTracker.track do + Sinatra::Base.new + assert_include MiddlewareTracker.used, Rack::Protection::PathTraversal + end + end + + it 'does not set up Rack::Protection::PathTraversal when disabling it' do + MiddlewareTracker.track do + Sinatra.new { set :protection, :except => :path_traversal }.new + assert_include MiddlewareTracker.used, Rack::Protection + assert !MiddlewareTracker.used.include?(Rack::Protection::PathTraversal) + end + end + + it 'sets up RemoteToken if sessions are enabled' do + MiddlewareTracker.track do + Sinatra.new { enable :sessions }.new + assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken + end + end + + it 'sets up RemoteToken if sessions are enabled with a custom session store' do + MiddlewareTracker.track do + Sinatra.new { + enable :sessions + set :session_store, Rack::Session::Pool + }.new + assert_include MiddlewareTracker.used, Rack::Session::Pool + assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken + end + end + + it 'does not set up RemoteToken if sessions are disabled' do + MiddlewareTracker.track do + Sinatra.new.new + assert !MiddlewareTracker.used.include?(Rack::Protection::RemoteToken) + end + end + + it 'sets up RemoteToken if it is configured to' do + MiddlewareTracker.track do + Sinatra.new { set :protection, :session => true }.new + assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken + end + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/old.rb b/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/old.rb new file mode 100644 index 0000000..10d9796 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_check_for_whether_falcon_i_14/old.rb @@ -0,0 +1,654 @@ +require File.expand_path('helper', __dir__) + +class SettingsTest < Minitest::Test + setup do + @base = Sinatra.new(Sinatra::Base) + @base.set :environment => :foo, :app_file => nil + + @application = Sinatra.new(Sinatra::Application) + @application.set :environment => :foo, :app_file => nil + end + + it 'sets settings to literal values' do + @base.set(:foo, 'bar') + assert @base.respond_to?(:foo) + assert_equal 'bar', @base.foo + end + + it 'sets settings to Procs' do + @base.set(:foo, Proc.new { 'baz' }) + assert @base.respond_to?(:foo) + assert_equal 'baz', @base.foo + end + + it 'sets settings using a block' do + @base.set(:foo){ 'baz' } + assert @base.respond_to?(:foo) + assert_equal 'baz', @base.foo + end + + it 'raises an error with a value and a block' do + assert_raises ArgumentError do + @base.set(:fiz, 'boom!'){ 'baz' } + end + assert !@base.respond_to?(:fiz) + end + + it 'raises an error without value and block' do + assert_raises(ArgumentError) { @base.set(:fiz) } + assert !@base.respond_to?(:fiz) + end + + it 'allows setting a value to the app class' do + @base.set :base, @base + assert @base.respond_to?(:base) + assert_equal @base, @base.base + end + + it 'raises an error with the app class as value and a block' do + assert_raises ArgumentError do + @base.set(:fiz, @base) { 'baz' } + end + assert !@base.respond_to?(:fiz) + end + + it "sets multiple settings with a Hash" do + @base.set :foo => 1234, + :bar => 'Hello World', + :baz => Proc.new { 'bizzle' } + assert_equal 1234, @base.foo + assert_equal 'Hello World', @base.bar + assert_equal 'bizzle', @base.baz + end + + it 'sets multiple settings using #each' do + @base.set [["foo", "bar"]] + assert_equal "bar", @base.foo + end + + it 'inherits settings methods when subclassed' do + @base.set :foo, 'bar' + @base.set :biz, Proc.new { 'baz' } + + sub = Class.new(@base) + assert sub.respond_to?(:foo) + assert_equal 'bar', sub.foo + assert sub.respond_to?(:biz) + assert_equal 'baz', sub.biz + end + + it 'overrides settings in subclass' do + @base.set :foo, 'bar' + @base.set :biz, Proc.new { 'baz' } + sub = Class.new(@base) + sub.set :foo, 'bling' + assert_equal 'bling', sub.foo + assert_equal 'bar', @base.foo + end + + it 'creates setter methods when first defined' do + @base.set :foo, 'bar' + assert @base.respond_to?('foo=') + @base.foo = 'biz' + assert_equal 'biz', @base.foo + end + + it 'creates predicate methods when first defined' do + @base.set :foo, 'hello world' + assert @base.respond_to?(:foo?) + assert @base.foo? + @base.set :foo, nil + assert !@base.foo? + end + + it 'uses existing setter methods if detected' do + class << @base + def foo + @foo + end + def foo=(value) + @foo = 'oops' + end + end + + @base.set :foo, 'bam' + assert_equal 'oops', @base.foo + end + + it 'merges values of multiple set calls if those are hashes' do + @base.set :foo, :a => 1 + sub = Class.new(@base) + sub.set :foo, :b => 2 + assert_equal({:a => 1, :b => 2}, sub.foo) + end + + it 'merging does not affect the superclass' do + @base.set :foo, :a => 1 + sub = Class.new(@base) + sub.set :foo, :b => 2 + assert_equal({:a => 1}, @base.foo) + end + + it 'is possible to change a value from a hash to something else' do + @base.set :foo, :a => 1 + @base.set :foo, :bar + assert_equal(:bar, @base.foo) + end + + it 'merges values with values of the superclass if those are hashes' do + @base.set :foo, :a => 1 + @base.set :foo, :b => 2 + assert_equal({:a => 1, :b => 2}, @base.foo) + end + + it "sets multiple settings to true with #enable" do + @base.enable :sessions, :foo, :bar + assert @base.sessions + assert @base.foo + assert @base.bar + end + + it "sets multiple settings to false with #disable" do + @base.disable :sessions, :foo, :bar + assert !@base.sessions + assert !@base.foo + assert !@base.bar + end + + it 'is accessible from instances via #settings' do + assert_equal :foo, @base.new!.settings.environment + end + + it 'is accessible from class via #settings' do + assert_equal :foo, @base.settings.environment + end + + describe 'default_content_type' do + it 'defaults to html' do + assert_equal 'text/html', @base.default_content_type + end + + it 'can be changed' do + @base.set :default_content_type, 'application/json' + @base.get('/') { '{"a":1}' } + @app = @base + get '/' + assert_equal 200, status + assert_equal 'application/json', response.content_type + end + + it 'can be disabled' do + @base.set :default_content_type, nil + @base.error(404) { "" } + @app = @base + get '/' + assert_equal 404, status + assert_nil response.content_type + assert_empty body + end + + it 'may emit content without a content-type (to be sniffed)' do + @base.set :default_content_type, nil + @base.get('/') { raise Sinatra::BadRequest, "This is a drill" } + @app = @base + get '/' + assert_equal 400, status + assert_nil response.content_type + assert_body "This is a drill" + end + end + + describe 'methodoverride' do + it 'is disabled on Base' do + assert ! @base.method_override? + end + + it 'is enabled on Application' do + assert @application.method_override? + end + + it 'enables MethodOverride middleware' do + @base.set :method_override, true + @base.put('/') { 'okay' } + @app = @base + post '/', {'_method'=>'PUT'}, {} + assert_equal 200, status + assert_equal 'okay', body + end + + it 'is backward compatible with methodoverride' do + assert ! @base.methodoverride? + @base.enable :methodoverride + assert @base.methodoverride? + end + + it 'ignores bundler/inline from callers' do + @application.stub(:caller, ->(_){ ['/path/to/bundler/inline.rb', $0] }) do + assert_equal File.expand_path($0), File.expand_path(@application.send(:caller_files).first) + end + end + end + + describe 'run' do + it 'is disabled on Base' do + assert ! @base.run? + end + + it 'is enabled on Application except in test environment' do + assert @application.run? + + @application.set :environment, :test + assert ! @application.run? + end + end + + describe 'raise_errors' do + it 'is enabled on Base only in test' do + assert ! @base.raise_errors? + + @base.set(:environment, :test) + assert @base.raise_errors? + end + + it 'is enabled on Application only in test' do + assert ! @application.raise_errors? + + @application.set(:environment, :test) + assert @application.raise_errors? + end + end + + describe 'show_exceptions' do + it 'is disabled on Base except under development' do + assert ! @base.show_exceptions? + @base.environment = :development + assert @base.show_exceptions? + end + + it 'is disabled on Application except in development' do + assert ! @application.show_exceptions? + + @application.set(:environment, :development) + assert @application.show_exceptions? + end + + it 'returns a friendly 500' do + klass = Sinatra.new(Sinatra::Application) + mock_app(klass) { + enable :show_exceptions + + get '/' do + raise StandardError + end + } + + get '/' + assert_equal 500, status + assert body.include?("StandardError") + assert body.include?("show_exceptions setting") + end + + it 'does not attempt to show unparseable query parameters' do + klass = Sinatra.new(Sinatra::Application) + mock_app(klass) { + enable :show_exceptions + + get '/' do + raise Sinatra::BadRequest + end + } + + get '/' + assert_equal 400, status + refute body.include?('
') + refute body.include?('
') + end + + it 'does not override app-specified error handling when set to :after_handler' do + ran = false + mock_app do + set :show_exceptions, :after_handler + error(RuntimeError) { ran = true } + get('/') { raise RuntimeError } + end + + get '/' + assert_equal 500, status + assert ran + end + + it 'does catch any other exceptions when set to :after_handler' do + ran = false + mock_app do + set :show_exceptions, :after_handler + error(RuntimeError) { ran = true } + get('/') { raise ArgumentError } + end + + get '/' + assert_equal 500, status + assert !ran + end + end + + describe 'dump_errors' do + it 'is disabled on Base in test' do + @base.environment = :test + assert ! @base.dump_errors? + @base.environment = :development + assert @base.dump_errors? + @base.environment = :production + assert @base.dump_errors? + end + + it 'dumps exception with backtrace to rack.errors' do + klass = Sinatra.new(Sinatra::Application) + + mock_app(klass) { + enable :dump_errors + disable :raise_errors + + error do + error = @env['rack.errors'].instance_variable_get(:@error) + error.rewind + + error.read + end + + get '/' do + raise + end + } + + get '/' + assert body.include?("RuntimeError") && body.include?("settings_test.rb") + end + + it 'does not dump 404 errors' do + klass = Sinatra.new(Sinatra::Application) + + mock_app(klass) { + enable :dump_errors + disable :raise_errors + + error do + error = @env['rack.errors'].instance_variable_get(:@error) + error.rewind + + error.read + end + + get '/' do + raise Sinatra::NotFound + end + } + + get '/' + assert !body.include?("NotFound") && !body.include?("settings_test.rb") + end + end + + describe 'sessions' do + it 'is disabled on Base' do + assert ! @base.sessions? + end + + it 'is disabled on Application' do + assert ! @application.sessions? + end + end + + describe 'logging' do + it 'is disabled on Base' do + assert ! @base.logging? + end + + it 'is enabled on Application except in test environment' do + assert @application.logging? + + @application.set :environment, :test + assert ! @application.logging + end + end + + describe 'static' do + it 'is disabled on Base by default' do + assert ! @base.static? + end + + it 'is enabled on Base when public_folder is set and exists' do + @base.set :environment, :development + @base.set :public_folder, __dir__ + assert @base.static? + end + + it 'is enabled on Base when root is set and root/public_folder exists' do + @base.set :environment, :development + @base.set :root, __dir__ + assert @base.static? + end + + it 'is disabled on Application by default' do + assert ! @application.static? + end + + it 'is enabled on Application when public_folder is set and exists' do + @application.set :environment, :development + @application.set :public_folder, __dir__ + assert @application.static? + end + + it 'is enabled on Application when root is set and root/public_folder exists' do + @application.set :environment, :development + @application.set :root, __dir__ + assert @application.static? + end + + it 'is possible to use Module#public' do + @base.send(:define_method, :foo) { } + @base.send(:private, :foo) + assert !@base.public_method_defined?(:foo) + @base.send(:public, :foo) + assert @base.public_method_defined?(:foo) + end + + it 'is possible to use the keyword public in a sinatra app' do + app = Sinatra.new do + private + def priv; end + public + def pub; end + end + assert !app.public_method_defined?(:priv) + assert app.public_method_defined?(:pub) + end + end + + describe 'bind' do + it 'defaults to 0.0.0.0' do + assert_equal '0.0.0.0', @base.bind + assert_equal '0.0.0.0', @application.bind + end + end + + describe 'port' do + it 'defaults to 4567' do + assert_equal 4567, @base.port + assert_equal 4567, @application.port + end + end + + describe 'server' do + it 'includes webrick' do + assert @base.server.include?('webrick') + assert @application.server.include?('webrick') + end + + it 'includes puma' do + assert @base.server.include?('puma') + assert @application.server.include?('puma') + end + + it 'includes falcon on mruby' do + if RUBY_ENGINE == 'ruby' + assert @base.server.include?('falcon') + assert @application.server.include?('falcon') + else + assert !@base.server.include?('falcon') + assert !@application.server.include?('falcon') + end + end + end + + describe 'app_file' do + it 'is nil for base classes' do + assert_nil Sinatra::Base.app_file + assert_nil Sinatra::Application.app_file + end + + it 'defaults to the file subclassing' do + assert_equal File.expand_path(__FILE__), Sinatra.new.app_file + end + end + + describe 'root' do + it 'is nil if app_file is not set' do + assert_nil @base.root + assert_nil @application.root + end + + it 'is equal to the expanded basename of app_file' do + @base.app_file = __FILE__ + assert_equal File.expand_path(__dir__), @base.root + + @application.app_file = __FILE__ + assert_equal File.expand_path(__dir__), @application.root + end + end + + describe 'views' do + it 'is nil if root is not set' do + assert_nil @base.views + assert_nil @application.views + end + + it 'is set to root joined with views/' do + @base.root = __dir__ + assert_equal __dir__ + "/views", @base.views + + @application.root = __dir__ + assert_equal __dir__ + "/views", @application.views + end + end + + describe 'public_folder' do + it 'is nil if root is not set' do + assert_nil @base.public_folder + assert_nil @application.public_folder + end + + it 'is set to root joined with public/' do + @base.root = __dir__ + assert_equal __dir__ + "/public", @base.public_folder + + @application.root = __dir__ + assert_equal __dir__ + "/public", @application.public_folder + end + end + + describe 'public_dir' do + it 'is an alias for public_folder' do + @base.public_dir = __dir__ + assert_equal __dir__, @base.public_dir + assert_equal @base.public_folder, @base.public_dir + + @application.public_dir = __dir__ + assert_equal __dir__, @application.public_dir + assert_equal @application.public_folder, @application.public_dir + end + end + + describe 'lock' do + it 'is disabled by default' do + assert ! @base.lock? + assert ! @application.lock? + end + end + + describe 'protection' do + class MiddlewareTracker < Rack::Builder + def self.track + Rack.send :remove_const, :Builder + Rack.const_set :Builder, MiddlewareTracker + MiddlewareTracker.used.clear + yield + ensure + Rack.send :remove_const, :Builder + Rack.const_set :Builder, MiddlewareTracker.superclass + end + + def self.used + @used ||= [] + end + + def use(middleware, *) + MiddlewareTracker.used << middleware + super + end + end + + it 'sets up Rack::Protection' do + MiddlewareTracker.track do + Sinatra::Base.new + assert_include MiddlewareTracker.used, Rack::Protection + end + end + + it 'sets up Rack::Protection::PathTraversal' do + MiddlewareTracker.track do + Sinatra::Base.new + assert_include MiddlewareTracker.used, Rack::Protection::PathTraversal + end + end + + it 'does not set up Rack::Protection::PathTraversal when disabling it' do + MiddlewareTracker.track do + Sinatra.new { set :protection, :except => :path_traversal }.new + assert_include MiddlewareTracker.used, Rack::Protection + assert !MiddlewareTracker.used.include?(Rack::Protection::PathTraversal) + end + end + + it 'sets up RemoteToken if sessions are enabled' do + MiddlewareTracker.track do + Sinatra.new { enable :sessions }.new + assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken + end + end + + it 'sets up RemoteToken if sessions are enabled with a custom session store' do + MiddlewareTracker.track do + Sinatra.new { + enable :sessions + set :session_store, Rack::Session::Pool + }.new + assert_include MiddlewareTracker.used, Rack::Session::Pool + assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken + end + end + + it 'does not set up RemoteToken if sessions are disabled' do + MiddlewareTracker.track do + Sinatra.new.new + assert !MiddlewareTracker.used.include?(Rack::Protection::RemoteToken) + end + end + + it 'sets up RemoteToken if it is configured to' do + MiddlewareTracker.track do + Sinatra.new { set :protection, :session => true }.new + assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken + end + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_ci_failures_15/expected.json b/tests/testdata/ruby_sinatra_fix_ci_failures_15/expected.json new file mode 100644 index 0000000..8f3ffdf --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_ci_failures_15/expected.json @@ -0,0 +1,1087 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2 + ], + "type": "begin", + "start_byte": 3825, + "end_byte": 3962 + }, + "parent": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3 + ], + "type": "assignment", + "start_byte": 3818, + "end_byte": 3962 + }, + "position": 2 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2, + 1 + ], + "type": "call", + "start_byte": 3845, + "end_byte": 3879 + }, + "parent": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2 + ], + "type": "begin", + "start_byte": 3825, + "end_byte": 3962 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2, + 3 + ], + "type": "end", + "label": "end", + "start_byte": 3959, + "end_byte": 3962 + }, + "parent": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2 + ], + "type": "begin", + "start_byte": 3825, + "end_byte": 3962 + }, + "position": 3 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2, + 2, + 1 + ], + "type": "exceptions", + "start_byte": 3899, + "end_byte": 3912 + }, + "parent": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2, + 2 + ], + "type": "rescue", + "start_byte": 3892, + "end_byte": 3946 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2, + 2, + 2 + ], + "type": "then", + "start_byte": 3912, + "end_byte": 3946 + }, + "parent": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2, + 2 + ], + "type": "rescue", + "start_byte": 3892, + "end_byte": 3946 + }, + "position": 2 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2 + ], + "type": "call", + "start_byte": 3825, + "end_byte": 3844 + }, + "parent": { + "tree": "after", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3, + 2, + 2, + 2 + ], + "type": "then", + "start_byte": 3912, + "end_byte": 3946 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 3, + 104, + 0, + 4, + 4, + 3, + 0, + 3, + 1, + 0, + 2, + 2, + 0, + 3, + 2, + 3 + ], + "type": "assignment", + "start_byte": 3818, + "end_byte": 3844 + }, + "old_position": 2, + "subtree": true, + "dest_start_byte": 3927, + "dest_end_byte": 3946 + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": -1 + }, + { + "left_line": -1, + "right_line": 126 + }, + { + "left_line": -1, + "right_line": 127 + }, + { + "left_line": -1, + "right_line": 128 + }, + { + "left_line": -1, + "right_line": 129 + }, + { + "left_line": -1, + "right_line": 130 + }, + { + "left_line": 127, + "right_line": 131 + }, + { + "left_line": 128, + "right_line": 132 + }, + { + "left_line": 129, + "right_line": 133 + }, + { + "left_line": 130, + "right_line": 134 + }, + { + "left_line": 131, + "right_line": 135 + }, + { + "left_line": 132, + "right_line": 136 + }, + { + "left_line": 133, + "right_line": 137 + }, + { + "left_line": 134, + "right_line": 138 + }, + { + "left_line": 135, + "right_line": 139 + }, + { + "left_line": 136, + "right_line": 140 + }, + { + "left_line": 137, + "right_line": 141 + }, + { + "left_line": 138, + "right_line": 142 + }, + { + "left_line": 139, + "right_line": 143 + }, + { + "left_line": 140, + "right_line": 144 + }, + { + "left_line": 141, + "right_line": 145 + }, + { + "left_line": 142, + "right_line": 146 + }, + { + "left_line": 143, + "right_line": 147 + }, + { + "left_line": 144, + "right_line": 148 + }, + { + "left_line": 145, + "right_line": 149 + }, + { + "left_line": 146, + "right_line": 150 + }, + { + "left_line": 147, + "right_line": 151 + }, + { + "left_line": 148, + "right_line": 152 + }, + { + "left_line": 149, + "right_line": 153 + }, + { + "left_line": 150, + "right_line": 154 + }, + { + "left_line": 151, + "right_line": 155 + }, + { + "left_line": 152, + "right_line": 156 + }, + { + "left_line": 153, + "right_line": 157 + }, + { + "left_line": 154, + "right_line": 158 + }, + { + "left_line": 155, + "right_line": 159 + }, + { + "left_line": 156, + "right_line": 160 + }, + { + "left_line": 157, + "right_line": 161 + }, + { + "left_line": 158, + "right_line": 162 + }, + { + "left_line": 159, + "right_line": 163 + }, + { + "left_line": 160, + "right_line": 164 + }, + { + "left_line": 161, + "right_line": 165 + }, + { + "left_line": 162, + "right_line": 166 + }, + { + "left_line": 163, + "right_line": 167 + }, + { + "left_line": 164, + "right_line": 168 + }, + { + "left_line": 165, + "right_line": 169 + }, + { + "left_line": 166, + "right_line": 170 + }, + { + "left_line": 167, + "right_line": 171 + }, + { + "left_line": 168, + "right_line": 172 + }, + { + "left_line": 169, + "right_line": 173 + }, + { + "left_line": 170, + "right_line": 174 + }, + { + "left_line": 171, + "right_line": 175 + }, + { + "left_line": 172, + "right_line": 176 + }, + { + "left_line": 173, + "right_line": 177 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_ci_failures_15/metadata.json b/tests/testdata/ruby_sinatra_fix_ci_failures_15/metadata.json new file mode 100644 index 0000000..01269db --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_ci_failures_15/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "04134fa830877f1b66d2955eaaa94a143e8dcb14", + "commit": "e6a2420e326fe29d029ffb64db7ce09e3fe9e99c", + "message": "Fix CI failures", + "author": "Jordan Owens", + "date": "2022-04-13T21:32:01-04:00", + "language": "ruby", + "files_changed": 2, + "insertions": 6, + "deletions": 2 +} diff --git a/tests/testdata/ruby_sinatra_fix_ci_failures_15/new.rb b/tests/testdata/ruby_sinatra_fix_ci_failures_15/new.rb new file mode 100644 index 0000000..971a794 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_ci_failures_15/new.rb @@ -0,0 +1,177 @@ +require 'sinatra/base' +require 'yaml' +require 'erb' + +module Sinatra + + # = Sinatra::ConfigFile + # + # Sinatra::ConfigFile is an extension that allows you to load the + # application's configuration from YAML files. It automatically detects if + # the files contain specific environment settings and it will use those + # corresponding to the current one. + # + # You can access those options through +settings+ within the application. If + # you try to get the value for a setting that hasn't been defined in the + # config file for the current environment, you will get whatever it was set + # to in the application. + # + # == Usage + # + # Once you have written your configurations to a YAML file you can tell the + # extension to load them. See below for more information about how these + # files are interpreted. + # + # For the examples, lets assume the following config.yml file: + # + # greeting: Welcome to my file configurable application + # + # === Classic Application + # + # require "sinatra" + # require "sinatra/config_file" + # + # config_file 'path/to/config.yml' + # + # get '/' do + # @greeting = settings.greeting + # haml :index + # end + # + # # The rest of your classic application code goes here... + # + # === Modular Application + # + # require "sinatra/base" + # require "sinatra/config_file" + # + # class MyApp < Sinatra::Base + # register Sinatra::ConfigFile + # + # config_file 'path/to/config.yml' + # + # get '/' do + # @greeting = settings.greeting + # haml :index + # end + # + # # The rest of your modular application code goes here... + # end + # + # === Config File Format + # + # In its most simple form this file is just a key-value list: + # + # foo: bar + # something: 42 + # nested: + # a: 1 + # b: 2 + # + # But it also can provide specific environment configuration. There are two + # ways to do that: at the file level and at the settings level. + # + # At the settings level (e.g. in 'path/to/config.yml'): + # + # development: + # foo: development + # bar: bar + # test: + # foo: test + # bar: bar + # production: + # foo: production + # bar: bar + # + # Or at the file level: + # + # foo: + # development: development + # test: test + # production: production + # bar: bar + # + # In either case, settings.foo will return the environment name, and + # settings.bar will return "bar". + # + # If you wish to provide defaults that may be shared among all the + # environments, this can be done by using a YAML alias, and then overwriting + # values in environments where appropriate: + # + # default: &common_settings + # foo: 'foo' + # bar: 'bar' + # + # production: + # <<: *common_settings + # bar: 'baz' # override the default value + # + module ConfigFile + + # When the extension is registered sets the +environments+ setting to the + # traditional environments: development, test and production. + def self.registered(base) + base.set :environments, %w[test production development] + end + + # Loads the configuration from the YAML files whose +paths+ are passed as + # arguments, filtering the settings for the current environment. Note that + # these +paths+ can actually be globs. + def config_file(*paths) + Dir.chdir(root || '.') do + paths.each do |pattern| + Dir.glob(pattern) do |file| + raise UnsupportedConfigType unless ['.yml', '.yaml', '.erb'].include?(File.extname(file)) + logger.info "loading config file '#{file}'" if logging? && respond_to?(:logger) + document = ERB.new(IO.read(file)).result + yaml = begin + YAML.load(document, aliases: true) + rescue ArgumentError + YAML.load(document) + end + config = config_for_env(yaml) + config.each_pair { |key, value| set(key, value) } + end + end + end + end + + class UnsupportedConfigType < Exception + def message + 'Invalid config file type, use .yml, .yaml or .erb' + end + end + + private + + # Given a +hash+ containing application configuration it returns + # settings applicable to the current environment. Note: It gives + # precedence to environment settings defined at the root-level. + def config_for_env(hash) + return from_environment_key(hash) if environment_keys?(hash) + + hash.each_with_object(IndifferentHash[]) do |(k, v), acc| + if environment_keys?(v) + acc.merge!(k => v[environment.to_s]) if v.key?(environment.to_s) + else + acc.merge!(k => v) + end + end + end + + # Given a +hash+ returns the settings corresponding to the current + # environment. + def from_environment_key(hash) + hash[environment.to_s] || hash[environment.to_sym] || {} + end + + # Returns true if supplied with a hash that has any recognized + # +environments+ in its root keys. + def environment_keys?(hash) + hash.is_a?(Hash) && hash.any? { |k, _| environments.include?(k.to_s) } + end + end + + register ConfigFile + Delegator.delegate :config_file +end diff --git a/tests/testdata/ruby_sinatra_fix_ci_failures_15/old.rb b/tests/testdata/ruby_sinatra_fix_ci_failures_15/old.rb new file mode 100644 index 0000000..701e99a --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_ci_failures_15/old.rb @@ -0,0 +1,173 @@ +require 'sinatra/base' +require 'yaml' +require 'erb' + +module Sinatra + + # = Sinatra::ConfigFile + # + # Sinatra::ConfigFile is an extension that allows you to load the + # application's configuration from YAML files. It automatically detects if + # the files contain specific environment settings and it will use those + # corresponding to the current one. + # + # You can access those options through +settings+ within the application. If + # you try to get the value for a setting that hasn't been defined in the + # config file for the current environment, you will get whatever it was set + # to in the application. + # + # == Usage + # + # Once you have written your configurations to a YAML file you can tell the + # extension to load them. See below for more information about how these + # files are interpreted. + # + # For the examples, lets assume the following config.yml file: + # + # greeting: Welcome to my file configurable application + # + # === Classic Application + # + # require "sinatra" + # require "sinatra/config_file" + # + # config_file 'path/to/config.yml' + # + # get '/' do + # @greeting = settings.greeting + # haml :index + # end + # + # # The rest of your classic application code goes here... + # + # === Modular Application + # + # require "sinatra/base" + # require "sinatra/config_file" + # + # class MyApp < Sinatra::Base + # register Sinatra::ConfigFile + # + # config_file 'path/to/config.yml' + # + # get '/' do + # @greeting = settings.greeting + # haml :index + # end + # + # # The rest of your modular application code goes here... + # end + # + # === Config File Format + # + # In its most simple form this file is just a key-value list: + # + # foo: bar + # something: 42 + # nested: + # a: 1 + # b: 2 + # + # But it also can provide specific environment configuration. There are two + # ways to do that: at the file level and at the settings level. + # + # At the settings level (e.g. in 'path/to/config.yml'): + # + # development: + # foo: development + # bar: bar + # test: + # foo: test + # bar: bar + # production: + # foo: production + # bar: bar + # + # Or at the file level: + # + # foo: + # development: development + # test: test + # production: production + # bar: bar + # + # In either case, settings.foo will return the environment name, and + # settings.bar will return "bar". + # + # If you wish to provide defaults that may be shared among all the + # environments, this can be done by using a YAML alias, and then overwriting + # values in environments where appropriate: + # + # default: &common_settings + # foo: 'foo' + # bar: 'bar' + # + # production: + # <<: *common_settings + # bar: 'baz' # override the default value + # + module ConfigFile + + # When the extension is registered sets the +environments+ setting to the + # traditional environments: development, test and production. + def self.registered(base) + base.set :environments, %w[test production development] + end + + # Loads the configuration from the YAML files whose +paths+ are passed as + # arguments, filtering the settings for the current environment. Note that + # these +paths+ can actually be globs. + def config_file(*paths) + Dir.chdir(root || '.') do + paths.each do |pattern| + Dir.glob(pattern) do |file| + raise UnsupportedConfigType unless ['.yml', '.yaml', '.erb'].include?(File.extname(file)) + logger.info "loading config file '#{file}'" if logging? && respond_to?(:logger) + document = ERB.new(IO.read(file)).result + yaml = YAML.load(document) + config = config_for_env(yaml) + config.each_pair { |key, value| set(key, value) } + end + end + end + end + + class UnsupportedConfigType < Exception + def message + 'Invalid config file type, use .yml, .yaml or .erb' + end + end + + private + + # Given a +hash+ containing application configuration it returns + # settings applicable to the current environment. Note: It gives + # precedence to environment settings defined at the root-level. + def config_for_env(hash) + return from_environment_key(hash) if environment_keys?(hash) + + hash.each_with_object(IndifferentHash[]) do |(k, v), acc| + if environment_keys?(v) + acc.merge!(k => v[environment.to_s]) if v.key?(environment.to_s) + else + acc.merge!(k => v) + end + end + end + + # Given a +hash+ returns the settings corresponding to the current + # environment. + def from_environment_key(hash) + hash[environment.to_s] || hash[environment.to_sym] || {} + end + + # Returns true if supplied with a hash that has any recognized + # +environments+ in its root keys. + def environment_keys?(hash) + hash.is_a?(Hash) && hash.any? { |k, _| environments.include?(k.to_s) } + end + end + + register ConfigFile + Delegator.delegate :config_file +end diff --git a/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/expected.json b/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/expected.json new file mode 100644 index 0000000..54d1306 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/expected.json @@ -0,0 +1,9242 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 4 + ], + "type": "comment", + "label": "# Set-based routing only iterates matching routes, so the per-iteration", + "start_byte": 33436, + "end_byte": 33507 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3 + ], + "type": "body_statement", + "start_byte": 32421, + "end_byte": 33826 + }, + "position": 4 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 5 + ], + "type": "comment", + "label": "# reset above is skipped after a route that set a content-type then passed.", + "start_byte": 33514, + "end_byte": 33589 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3 + ], + "type": "body_statement", + "start_byte": 32421, + "end_byte": 33826 + }, + "position": 5 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 6 + ], + "type": "comment", + "label": "# Clear it so the pass block starts clean, as the legacy all-routes scan did.", + "start_byte": 33596, + "end_byte": 33673 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3 + ], + "type": "body_statement", + "start_byte": 32421, + "end_byte": 33826 + }, + "position": 6 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7 + ], + "type": "if", + "start_byte": 33680, + "end_byte": 33806 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3 + ], + "type": "body_statement", + "start_byte": 32421, + "end_byte": 33826 + }, + "position": 7 + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 4, + 2 + ], + "type": "identifier", + "label": "pass_block", + "start_byte": 33463, + "end_byte": 33473 + } + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7, + 1 + ], + "type": "identifier", + "label": "pass_block", + "start_byte": 33683, + "end_byte": 33693 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7 + ], + "type": "if", + "start_byte": 33680, + "end_byte": 33806 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7, + 2 + ], + "type": "then", + "start_byte": 33693, + "end_byte": 33796 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7 + ], + "type": "if", + "start_byte": 33680, + "end_byte": 33806 + }, + "position": 2 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7, + 3 + ], + "type": "end", + "label": "end", + "start_byte": 33803, + "end_byte": 33806 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7 + ], + "type": "if", + "start_byte": 33680, + "end_byte": 33806 + }, + "position": 3 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7, + 2, + 0 + ], + "type": "unless_modifier", + "start_byte": 33702, + "end_byte": 33764 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7, + 2 + ], + "type": "then", + "start_byte": 33693, + "end_byte": 33796 + }, + "position": 0, + "subtree": true + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 4, + 0 + ], + "type": "call", + "start_byte": 33436, + "end_byte": 33459 + }, + "parent": { + "tree": "after", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 7, + 2 + ], + "type": "then", + "start_byte": 33693, + "end_byte": 33796 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 4 + ], + "type": "if_modifier", + "start_byte": 33436, + "end_byte": 33473 + }, + "old_position": 0, + "subtree": true, + "dest_start_byte": 33773, + "dest_end_byte": 33796 + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 20, + 4, + 49, + 2, + 28, + 3, + 4, + 1 + ], + "type": "if", + "label": "if", + "start_byte": 33460, + "end_byte": 33462 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": 277, + "right_line": 277 + }, + { + "left_line": 278, + "right_line": 278 + }, + { + "left_line": 279, + "right_line": 279 + }, + { + "left_line": 280, + "right_line": 280 + }, + { + "left_line": 281, + "right_line": 281 + }, + { + "left_line": 282, + "right_line": 282 + }, + { + "left_line": 283, + "right_line": 283 + }, + { + "left_line": 284, + "right_line": 284 + }, + { + "left_line": 285, + "right_line": 285 + }, + { + "left_line": 286, + "right_line": 286 + }, + { + "left_line": 287, + "right_line": 287 + }, + { + "left_line": 288, + "right_line": 288 + }, + { + "left_line": 289, + "right_line": 289 + }, + { + "left_line": 290, + "right_line": 290 + }, + { + "left_line": 291, + "right_line": 291 + }, + { + "left_line": 292, + "right_line": 292 + }, + { + "left_line": 293, + "right_line": 293 + }, + { + "left_line": 294, + "right_line": 294 + }, + { + "left_line": 295, + "right_line": 295 + }, + { + "left_line": 296, + "right_line": 296 + }, + { + "left_line": 297, + "right_line": 297 + }, + { + "left_line": 298, + "right_line": 298 + }, + { + "left_line": 299, + "right_line": 299 + }, + { + "left_line": 300, + "right_line": 300 + }, + { + "left_line": 301, + "right_line": 301 + }, + { + "left_line": 302, + "right_line": 302 + }, + { + "left_line": 303, + "right_line": 303 + }, + { + "left_line": 304, + "right_line": 304 + }, + { + "left_line": 305, + "right_line": 305 + }, + { + "left_line": 306, + "right_line": 306 + }, + { + "left_line": 307, + "right_line": 307 + }, + { + "left_line": 308, + "right_line": 308 + }, + { + "left_line": 309, + "right_line": 309 + }, + { + "left_line": 310, + "right_line": 310 + }, + { + "left_line": 311, + "right_line": 311 + }, + { + "left_line": 312, + "right_line": 312 + }, + { + "left_line": 313, + "right_line": 313 + }, + { + "left_line": 314, + "right_line": 314 + }, + { + "left_line": 315, + "right_line": 315 + }, + { + "left_line": 316, + "right_line": 316 + }, + { + "left_line": 317, + "right_line": 317 + }, + { + "left_line": 318, + "right_line": 318 + }, + { + "left_line": 319, + "right_line": 319 + }, + { + "left_line": 320, + "right_line": 320 + }, + { + "left_line": 321, + "right_line": 321 + }, + { + "left_line": 322, + "right_line": 322 + }, + { + "left_line": 323, + "right_line": 323 + }, + { + "left_line": 324, + "right_line": 324 + }, + { + "left_line": 325, + "right_line": 325 + }, + { + "left_line": 326, + "right_line": 326 + }, + { + "left_line": 327, + "right_line": 327 + }, + { + "left_line": 328, + "right_line": 328 + }, + { + "left_line": 329, + "right_line": 329 + }, + { + "left_line": 330, + "right_line": 330 + }, + { + "left_line": 331, + "right_line": 331 + }, + { + "left_line": 332, + "right_line": 332 + }, + { + "left_line": 333, + "right_line": 333 + }, + { + "left_line": 334, + "right_line": 334 + }, + { + "left_line": 335, + "right_line": 335 + }, + { + "left_line": 336, + "right_line": 336 + }, + { + "left_line": 337, + "right_line": 337 + }, + { + "left_line": 338, + "right_line": 338 + }, + { + "left_line": 339, + "right_line": 339 + }, + { + "left_line": 340, + "right_line": 340 + }, + { + "left_line": 341, + "right_line": 341 + }, + { + "left_line": 342, + "right_line": 342 + }, + { + "left_line": 343, + "right_line": 343 + }, + { + "left_line": 344, + "right_line": 344 + }, + { + "left_line": 345, + "right_line": 345 + }, + { + "left_line": 346, + "right_line": 346 + }, + { + "left_line": 347, + "right_line": 347 + }, + { + "left_line": 348, + "right_line": 348 + }, + { + "left_line": 349, + "right_line": 349 + }, + { + "left_line": 350, + "right_line": 350 + }, + { + "left_line": 351, + "right_line": 351 + }, + { + "left_line": 352, + "right_line": 352 + }, + { + "left_line": 353, + "right_line": 353 + }, + { + "left_line": 354, + "right_line": 354 + }, + { + "left_line": 355, + "right_line": 355 + }, + { + "left_line": 356, + "right_line": 356 + }, + { + "left_line": 357, + "right_line": 357 + }, + { + "left_line": 358, + "right_line": 358 + }, + { + "left_line": 359, + "right_line": 359 + }, + { + "left_line": 360, + "right_line": 360 + }, + { + "left_line": 361, + "right_line": 361 + }, + { + "left_line": 362, + "right_line": 362 + }, + { + "left_line": 363, + "right_line": 363 + }, + { + "left_line": 364, + "right_line": 364 + }, + { + "left_line": 365, + "right_line": 365 + }, + { + "left_line": 366, + "right_line": 366 + }, + { + "left_line": 367, + "right_line": 367 + }, + { + "left_line": 368, + "right_line": 368 + }, + { + "left_line": 369, + "right_line": 369 + }, + { + "left_line": 370, + "right_line": 370 + }, + { + "left_line": 371, + "right_line": 371 + }, + { + "left_line": 372, + "right_line": 372 + }, + { + "left_line": 373, + "right_line": 373 + }, + { + "left_line": 374, + "right_line": 374 + }, + { + "left_line": 375, + "right_line": 375 + }, + { + "left_line": 376, + "right_line": 376 + }, + { + "left_line": 377, + "right_line": 377 + }, + { + "left_line": 378, + "right_line": 378 + }, + { + "left_line": 379, + "right_line": 379 + }, + { + "left_line": 380, + "right_line": 380 + }, + { + "left_line": 381, + "right_line": 381 + }, + { + "left_line": 382, + "right_line": 382 + }, + { + "left_line": 383, + "right_line": 383 + }, + { + "left_line": 384, + "right_line": 384 + }, + { + "left_line": 385, + "right_line": 385 + }, + { + "left_line": 386, + "right_line": 386 + }, + { + "left_line": 387, + "right_line": 387 + }, + { + "left_line": 388, + "right_line": 388 + }, + { + "left_line": 389, + "right_line": 389 + }, + { + "left_line": 390, + "right_line": 390 + }, + { + "left_line": 391, + "right_line": 391 + }, + { + "left_line": 392, + "right_line": 392 + }, + { + "left_line": 393, + "right_line": 393 + }, + { + "left_line": 394, + "right_line": 394 + }, + { + "left_line": 395, + "right_line": 395 + }, + { + "left_line": 396, + "right_line": 396 + }, + { + "left_line": 397, + "right_line": 397 + }, + { + "left_line": 398, + "right_line": 398 + }, + { + "left_line": 399, + "right_line": 399 + }, + { + "left_line": 400, + "right_line": 400 + }, + { + "left_line": 401, + "right_line": 401 + }, + { + "left_line": 402, + "right_line": 402 + }, + { + "left_line": 403, + "right_line": 403 + }, + { + "left_line": 404, + "right_line": 404 + }, + { + "left_line": 405, + "right_line": 405 + }, + { + "left_line": 406, + "right_line": 406 + }, + { + "left_line": 407, + "right_line": 407 + }, + { + "left_line": 408, + "right_line": 408 + }, + { + "left_line": 409, + "right_line": 409 + }, + { + "left_line": 410, + "right_line": 410 + }, + { + "left_line": 411, + "right_line": 411 + }, + { + "left_line": 412, + "right_line": 412 + }, + { + "left_line": 413, + "right_line": 413 + }, + { + "left_line": 414, + "right_line": 414 + }, + { + "left_line": 415, + "right_line": 415 + }, + { + "left_line": 416, + "right_line": 416 + }, + { + "left_line": 417, + "right_line": 417 + }, + { + "left_line": 418, + "right_line": 418 + }, + { + "left_line": 419, + "right_line": 419 + }, + { + "left_line": 420, + "right_line": 420 + }, + { + "left_line": 421, + "right_line": 421 + }, + { + "left_line": 422, + "right_line": 422 + }, + { + "left_line": 423, + "right_line": 423 + }, + { + "left_line": 424, + "right_line": 424 + }, + { + "left_line": 425, + "right_line": 425 + }, + { + "left_line": 426, + "right_line": 426 + }, + { + "left_line": 427, + "right_line": 427 + }, + { + "left_line": 428, + "right_line": 428 + }, + { + "left_line": 429, + "right_line": 429 + }, + { + "left_line": 430, + "right_line": 430 + }, + { + "left_line": 431, + "right_line": 431 + }, + { + "left_line": 432, + "right_line": 432 + }, + { + "left_line": 433, + "right_line": 433 + }, + { + "left_line": 434, + "right_line": 434 + }, + { + "left_line": 435, + "right_line": 435 + }, + { + "left_line": 436, + "right_line": 436 + }, + { + "left_line": 437, + "right_line": 437 + }, + { + "left_line": 438, + "right_line": 438 + }, + { + "left_line": 439, + "right_line": 439 + }, + { + "left_line": 440, + "right_line": 440 + }, + { + "left_line": 441, + "right_line": 441 + }, + { + "left_line": 442, + "right_line": 442 + }, + { + "left_line": 443, + "right_line": 443 + }, + { + "left_line": 444, + "right_line": 444 + }, + { + "left_line": 445, + "right_line": 445 + }, + { + "left_line": 446, + "right_line": 446 + }, + { + "left_line": 447, + "right_line": 447 + }, + { + "left_line": 448, + "right_line": 448 + }, + { + "left_line": 449, + "right_line": 449 + }, + { + "left_line": 450, + "right_line": 450 + }, + { + "left_line": 451, + "right_line": 451 + }, + { + "left_line": 452, + "right_line": 452 + }, + { + "left_line": 453, + "right_line": 453 + }, + { + "left_line": 454, + "right_line": 454 + }, + { + "left_line": 455, + "right_line": 455 + }, + { + "left_line": 456, + "right_line": 456 + }, + { + "left_line": 457, + "right_line": 457 + }, + { + "left_line": 458, + "right_line": 458 + }, + { + "left_line": 459, + "right_line": 459 + }, + { + "left_line": 460, + "right_line": 460 + }, + { + "left_line": 461, + "right_line": 461 + }, + { + "left_line": 462, + "right_line": 462 + }, + { + "left_line": 463, + "right_line": 463 + }, + { + "left_line": 464, + "right_line": 464 + }, + { + "left_line": 465, + "right_line": 465 + }, + { + "left_line": 466, + "right_line": 466 + }, + { + "left_line": 467, + "right_line": 467 + }, + { + "left_line": 468, + "right_line": 468 + }, + { + "left_line": 469, + "right_line": 469 + }, + { + "left_line": 470, + "right_line": 470 + }, + { + "left_line": 471, + "right_line": 471 + }, + { + "left_line": 472, + "right_line": 472 + }, + { + "left_line": 473, + "right_line": 473 + }, + { + "left_line": 474, + "right_line": 474 + }, + { + "left_line": 475, + "right_line": 475 + }, + { + "left_line": 476, + "right_line": 476 + }, + { + "left_line": 477, + "right_line": 477 + }, + { + "left_line": 478, + "right_line": 478 + }, + { + "left_line": 479, + "right_line": 479 + }, + { + "left_line": 480, + "right_line": 480 + }, + { + "left_line": 481, + "right_line": 481 + }, + { + "left_line": 482, + "right_line": 482 + }, + { + "left_line": 483, + "right_line": 483 + }, + { + "left_line": 484, + "right_line": 484 + }, + { + "left_line": 485, + "right_line": 485 + }, + { + "left_line": 486, + "right_line": 486 + }, + { + "left_line": 487, + "right_line": 487 + }, + { + "left_line": 488, + "right_line": 488 + }, + { + "left_line": 489, + "right_line": 489 + }, + { + "left_line": 490, + "right_line": 490 + }, + { + "left_line": 491, + "right_line": 491 + }, + { + "left_line": 492, + "right_line": 492 + }, + { + "left_line": 493, + "right_line": 493 + }, + { + "left_line": 494, + "right_line": 494 + }, + { + "left_line": 495, + "right_line": 495 + }, + { + "left_line": 496, + "right_line": 496 + }, + { + "left_line": 497, + "right_line": 497 + }, + { + "left_line": 498, + "right_line": 498 + }, + { + "left_line": 499, + "right_line": 499 + }, + { + "left_line": 500, + "right_line": 500 + }, + { + "left_line": 501, + "right_line": 501 + }, + { + "left_line": 502, + "right_line": 502 + }, + { + "left_line": 503, + "right_line": 503 + }, + { + "left_line": 504, + "right_line": 504 + }, + { + "left_line": 505, + "right_line": 505 + }, + { + "left_line": 506, + "right_line": 506 + }, + { + "left_line": 507, + "right_line": 507 + }, + { + "left_line": 508, + "right_line": 508 + }, + { + "left_line": 509, + "right_line": 509 + }, + { + "left_line": 510, + "right_line": 510 + }, + { + "left_line": 511, + "right_line": 511 + }, + { + "left_line": 512, + "right_line": 512 + }, + { + "left_line": 513, + "right_line": 513 + }, + { + "left_line": 514, + "right_line": 514 + }, + { + "left_line": 515, + "right_line": 515 + }, + { + "left_line": 516, + "right_line": 516 + }, + { + "left_line": 517, + "right_line": 517 + }, + { + "left_line": 518, + "right_line": 518 + }, + { + "left_line": 519, + "right_line": 519 + }, + { + "left_line": 520, + "right_line": 520 + }, + { + "left_line": 521, + "right_line": 521 + }, + { + "left_line": 522, + "right_line": 522 + }, + { + "left_line": 523, + "right_line": 523 + }, + { + "left_line": 524, + "right_line": 524 + }, + { + "left_line": 525, + "right_line": 525 + }, + { + "left_line": 526, + "right_line": 526 + }, + { + "left_line": 527, + "right_line": 527 + }, + { + "left_line": 528, + "right_line": 528 + }, + { + "left_line": 529, + "right_line": 529 + }, + { + "left_line": 530, + "right_line": 530 + }, + { + "left_line": 531, + "right_line": 531 + }, + { + "left_line": 532, + "right_line": 532 + }, + { + "left_line": 533, + "right_line": 533 + }, + { + "left_line": 534, + "right_line": 534 + }, + { + "left_line": 535, + "right_line": 535 + }, + { + "left_line": 536, + "right_line": 536 + }, + { + "left_line": 537, + "right_line": 537 + }, + { + "left_line": 538, + "right_line": 538 + }, + { + "left_line": 539, + "right_line": 539 + }, + { + "left_line": 540, + "right_line": 540 + }, + { + "left_line": 541, + "right_line": 541 + }, + { + "left_line": 542, + "right_line": 542 + }, + { + "left_line": 543, + "right_line": 543 + }, + { + "left_line": 544, + "right_line": 544 + }, + { + "left_line": 545, + "right_line": 545 + }, + { + "left_line": 546, + "right_line": 546 + }, + { + "left_line": 547, + "right_line": 547 + }, + { + "left_line": 548, + "right_line": 548 + }, + { + "left_line": 549, + "right_line": 549 + }, + { + "left_line": 550, + "right_line": 550 + }, + { + "left_line": 551, + "right_line": 551 + }, + { + "left_line": 552, + "right_line": 552 + }, + { + "left_line": 553, + "right_line": 553 + }, + { + "left_line": 554, + "right_line": 554 + }, + { + "left_line": 555, + "right_line": 555 + }, + { + "left_line": 556, + "right_line": 556 + }, + { + "left_line": 557, + "right_line": 557 + }, + { + "left_line": 558, + "right_line": 558 + }, + { + "left_line": 559, + "right_line": 559 + }, + { + "left_line": 560, + "right_line": 560 + }, + { + "left_line": 561, + "right_line": 561 + }, + { + "left_line": 562, + "right_line": 562 + }, + { + "left_line": 563, + "right_line": 563 + }, + { + "left_line": 564, + "right_line": 564 + }, + { + "left_line": 565, + "right_line": 565 + }, + { + "left_line": 566, + "right_line": 566 + }, + { + "left_line": 567, + "right_line": 567 + }, + { + "left_line": 568, + "right_line": 568 + }, + { + "left_line": 569, + "right_line": 569 + }, + { + "left_line": 570, + "right_line": 570 + }, + { + "left_line": 571, + "right_line": 571 + }, + { + "left_line": 572, + "right_line": 572 + }, + { + "left_line": 573, + "right_line": 573 + }, + { + "left_line": 574, + "right_line": 574 + }, + { + "left_line": 575, + "right_line": 575 + }, + { + "left_line": 576, + "right_line": 576 + }, + { + "left_line": 577, + "right_line": 577 + }, + { + "left_line": 578, + "right_line": 578 + }, + { + "left_line": 579, + "right_line": 579 + }, + { + "left_line": 580, + "right_line": 580 + }, + { + "left_line": 581, + "right_line": 581 + }, + { + "left_line": 582, + "right_line": 582 + }, + { + "left_line": 583, + "right_line": 583 + }, + { + "left_line": 584, + "right_line": 584 + }, + { + "left_line": 585, + "right_line": 585 + }, + { + "left_line": 586, + "right_line": 586 + }, + { + "left_line": 587, + "right_line": 587 + }, + { + "left_line": 588, + "right_line": 588 + }, + { + "left_line": 589, + "right_line": 589 + }, + { + "left_line": 590, + "right_line": 590 + }, + { + "left_line": 591, + "right_line": 591 + }, + { + "left_line": 592, + "right_line": 592 + }, + { + "left_line": 593, + "right_line": 593 + }, + { + "left_line": 594, + "right_line": 594 + }, + { + "left_line": 595, + "right_line": 595 + }, + { + "left_line": 596, + "right_line": 596 + }, + { + "left_line": 597, + "right_line": 597 + }, + { + "left_line": 598, + "right_line": 598 + }, + { + "left_line": 599, + "right_line": 599 + }, + { + "left_line": 600, + "right_line": 600 + }, + { + "left_line": 601, + "right_line": 601 + }, + { + "left_line": 602, + "right_line": 602 + }, + { + "left_line": 603, + "right_line": 603 + }, + { + "left_line": 604, + "right_line": 604 + }, + { + "left_line": 605, + "right_line": 605 + }, + { + "left_line": 606, + "right_line": 606 + }, + { + "left_line": 607, + "right_line": 607 + }, + { + "left_line": 608, + "right_line": 608 + }, + { + "left_line": 609, + "right_line": 609 + }, + { + "left_line": 610, + "right_line": 610 + }, + { + "left_line": 611, + "right_line": 611 + }, + { + "left_line": 612, + "right_line": 612 + }, + { + "left_line": 613, + "right_line": 613 + }, + { + "left_line": 614, + "right_line": 614 + }, + { + "left_line": 615, + "right_line": 615 + }, + { + "left_line": 616, + "right_line": 616 + }, + { + "left_line": 617, + "right_line": 617 + }, + { + "left_line": 618, + "right_line": 618 + }, + { + "left_line": 619, + "right_line": 619 + }, + { + "left_line": 620, + "right_line": 620 + }, + { + "left_line": 621, + "right_line": 621 + }, + { + "left_line": 622, + "right_line": 622 + }, + { + "left_line": 623, + "right_line": 623 + }, + { + "left_line": 624, + "right_line": 624 + }, + { + "left_line": 625, + "right_line": 625 + }, + { + "left_line": 626, + "right_line": 626 + }, + { + "left_line": 627, + "right_line": 627 + }, + { + "left_line": 628, + "right_line": 628 + }, + { + "left_line": 629, + "right_line": 629 + }, + { + "left_line": 630, + "right_line": 630 + }, + { + "left_line": 631, + "right_line": 631 + }, + { + "left_line": 632, + "right_line": 632 + }, + { + "left_line": 633, + "right_line": 633 + }, + { + "left_line": 634, + "right_line": 634 + }, + { + "left_line": 635, + "right_line": 635 + }, + { + "left_line": 636, + "right_line": 636 + }, + { + "left_line": 637, + "right_line": 637 + }, + { + "left_line": 638, + "right_line": 638 + }, + { + "left_line": 639, + "right_line": 639 + }, + { + "left_line": 640, + "right_line": 640 + }, + { + "left_line": 641, + "right_line": 641 + }, + { + "left_line": 642, + "right_line": 642 + }, + { + "left_line": 643, + "right_line": 643 + }, + { + "left_line": 644, + "right_line": 644 + }, + { + "left_line": 645, + "right_line": 645 + }, + { + "left_line": 646, + "right_line": 646 + }, + { + "left_line": 647, + "right_line": 647 + }, + { + "left_line": 648, + "right_line": 648 + }, + { + "left_line": 649, + "right_line": 649 + }, + { + "left_line": 650, + "right_line": 650 + }, + { + "left_line": 651, + "right_line": 651 + }, + { + "left_line": 652, + "right_line": 652 + }, + { + "left_line": 653, + "right_line": 653 + }, + { + "left_line": 654, + "right_line": 654 + }, + { + "left_line": 655, + "right_line": 655 + }, + { + "left_line": 656, + "right_line": 656 + }, + { + "left_line": 657, + "right_line": 657 + }, + { + "left_line": 658, + "right_line": 658 + }, + { + "left_line": 659, + "right_line": 659 + }, + { + "left_line": 660, + "right_line": 660 + }, + { + "left_line": 661, + "right_line": 661 + }, + { + "left_line": 662, + "right_line": 662 + }, + { + "left_line": 663, + "right_line": 663 + }, + { + "left_line": 664, + "right_line": 664 + }, + { + "left_line": 665, + "right_line": 665 + }, + { + "left_line": 666, + "right_line": 666 + }, + { + "left_line": 667, + "right_line": 667 + }, + { + "left_line": 668, + "right_line": 668 + }, + { + "left_line": 669, + "right_line": 669 + }, + { + "left_line": 670, + "right_line": 670 + }, + { + "left_line": 671, + "right_line": 671 + }, + { + "left_line": 672, + "right_line": 672 + }, + { + "left_line": 673, + "right_line": 673 + }, + { + "left_line": 674, + "right_line": 674 + }, + { + "left_line": 675, + "right_line": 675 + }, + { + "left_line": 676, + "right_line": 676 + }, + { + "left_line": 677, + "right_line": 677 + }, + { + "left_line": 678, + "right_line": 678 + }, + { + "left_line": 679, + "right_line": 679 + }, + { + "left_line": 680, + "right_line": 680 + }, + { + "left_line": 681, + "right_line": 681 + }, + { + "left_line": 682, + "right_line": 682 + }, + { + "left_line": 683, + "right_line": 683 + }, + { + "left_line": 684, + "right_line": 684 + }, + { + "left_line": 685, + "right_line": 685 + }, + { + "left_line": 686, + "right_line": 686 + }, + { + "left_line": 687, + "right_line": 687 + }, + { + "left_line": 688, + "right_line": 688 + }, + { + "left_line": 689, + "right_line": 689 + }, + { + "left_line": 690, + "right_line": 690 + }, + { + "left_line": 691, + "right_line": 691 + }, + { + "left_line": 692, + "right_line": 692 + }, + { + "left_line": 693, + "right_line": 693 + }, + { + "left_line": 694, + "right_line": 694 + }, + { + "left_line": 695, + "right_line": 695 + }, + { + "left_line": 696, + "right_line": 696 + }, + { + "left_line": 697, + "right_line": 697 + }, + { + "left_line": 698, + "right_line": 698 + }, + { + "left_line": 699, + "right_line": 699 + }, + { + "left_line": 700, + "right_line": 700 + }, + { + "left_line": 701, + "right_line": 701 + }, + { + "left_line": 702, + "right_line": 702 + }, + { + "left_line": 703, + "right_line": 703 + }, + { + "left_line": 704, + "right_line": 704 + }, + { + "left_line": 705, + "right_line": 705 + }, + { + "left_line": 706, + "right_line": 706 + }, + { + "left_line": 707, + "right_line": 707 + }, + { + "left_line": 708, + "right_line": 708 + }, + { + "left_line": 709, + "right_line": 709 + }, + { + "left_line": 710, + "right_line": 710 + }, + { + "left_line": 711, + "right_line": 711 + }, + { + "left_line": 712, + "right_line": 712 + }, + { + "left_line": 713, + "right_line": 713 + }, + { + "left_line": 714, + "right_line": 714 + }, + { + "left_line": 715, + "right_line": 715 + }, + { + "left_line": 716, + "right_line": 716 + }, + { + "left_line": 717, + "right_line": 717 + }, + { + "left_line": 718, + "right_line": 718 + }, + { + "left_line": 719, + "right_line": 719 + }, + { + "left_line": 720, + "right_line": 720 + }, + { + "left_line": 721, + "right_line": 721 + }, + { + "left_line": 722, + "right_line": 722 + }, + { + "left_line": 723, + "right_line": 723 + }, + { + "left_line": 724, + "right_line": 724 + }, + { + "left_line": 725, + "right_line": 725 + }, + { + "left_line": 726, + "right_line": 726 + }, + { + "left_line": 727, + "right_line": 727 + }, + { + "left_line": 728, + "right_line": 728 + }, + { + "left_line": 729, + "right_line": 729 + }, + { + "left_line": 730, + "right_line": 730 + }, + { + "left_line": 731, + "right_line": 731 + }, + { + "left_line": 732, + "right_line": 732 + }, + { + "left_line": 733, + "right_line": 733 + }, + { + "left_line": 734, + "right_line": 734 + }, + { + "left_line": 735, + "right_line": 735 + }, + { + "left_line": 736, + "right_line": 736 + }, + { + "left_line": 737, + "right_line": 737 + }, + { + "left_line": 738, + "right_line": 738 + }, + { + "left_line": 739, + "right_line": 739 + }, + { + "left_line": 740, + "right_line": 740 + }, + { + "left_line": 741, + "right_line": 741 + }, + { + "left_line": 742, + "right_line": 742 + }, + { + "left_line": 743, + "right_line": 743 + }, + { + "left_line": 744, + "right_line": 744 + }, + { + "left_line": 745, + "right_line": 745 + }, + { + "left_line": 746, + "right_line": 746 + }, + { + "left_line": 747, + "right_line": 747 + }, + { + "left_line": 748, + "right_line": 748 + }, + { + "left_line": 749, + "right_line": 749 + }, + { + "left_line": 750, + "right_line": 750 + }, + { + "left_line": 751, + "right_line": 751 + }, + { + "left_line": 752, + "right_line": 752 + }, + { + "left_line": 753, + "right_line": 753 + }, + { + "left_line": 754, + "right_line": 754 + }, + { + "left_line": 755, + "right_line": 755 + }, + { + "left_line": 756, + "right_line": 756 + }, + { + "left_line": 757, + "right_line": 757 + }, + { + "left_line": 758, + "right_line": 758 + }, + { + "left_line": 759, + "right_line": 759 + }, + { + "left_line": 760, + "right_line": 760 + }, + { + "left_line": 761, + "right_line": 761 + }, + { + "left_line": 762, + "right_line": 762 + }, + { + "left_line": 763, + "right_line": 763 + }, + { + "left_line": 764, + "right_line": 764 + }, + { + "left_line": 765, + "right_line": 765 + }, + { + "left_line": 766, + "right_line": 766 + }, + { + "left_line": 767, + "right_line": 767 + }, + { + "left_line": 768, + "right_line": 768 + }, + { + "left_line": 769, + "right_line": 769 + }, + { + "left_line": 770, + "right_line": 770 + }, + { + "left_line": 771, + "right_line": 771 + }, + { + "left_line": 772, + "right_line": 772 + }, + { + "left_line": 773, + "right_line": 773 + }, + { + "left_line": 774, + "right_line": 774 + }, + { + "left_line": 775, + "right_line": 775 + }, + { + "left_line": 776, + "right_line": 776 + }, + { + "left_line": 777, + "right_line": 777 + }, + { + "left_line": 778, + "right_line": 778 + }, + { + "left_line": 779, + "right_line": 779 + }, + { + "left_line": 780, + "right_line": 780 + }, + { + "left_line": 781, + "right_line": 781 + }, + { + "left_line": 782, + "right_line": 782 + }, + { + "left_line": 783, + "right_line": 783 + }, + { + "left_line": 784, + "right_line": 784 + }, + { + "left_line": 785, + "right_line": 785 + }, + { + "left_line": 786, + "right_line": 786 + }, + { + "left_line": 787, + "right_line": 787 + }, + { + "left_line": 788, + "right_line": 788 + }, + { + "left_line": 789, + "right_line": 789 + }, + { + "left_line": 790, + "right_line": 790 + }, + { + "left_line": 791, + "right_line": 791 + }, + { + "left_line": 792, + "right_line": 792 + }, + { + "left_line": 793, + "right_line": 793 + }, + { + "left_line": 794, + "right_line": 794 + }, + { + "left_line": 795, + "right_line": 795 + }, + { + "left_line": 796, + "right_line": 796 + }, + { + "left_line": 797, + "right_line": 797 + }, + { + "left_line": 798, + "right_line": 798 + }, + { + "left_line": 799, + "right_line": 799 + }, + { + "left_line": 800, + "right_line": 800 + }, + { + "left_line": 801, + "right_line": 801 + }, + { + "left_line": 802, + "right_line": 802 + }, + { + "left_line": 803, + "right_line": 803 + }, + { + "left_line": 804, + "right_line": 804 + }, + { + "left_line": 805, + "right_line": 805 + }, + { + "left_line": 806, + "right_line": 806 + }, + { + "left_line": 807, + "right_line": 807 + }, + { + "left_line": 808, + "right_line": 808 + }, + { + "left_line": 809, + "right_line": 809 + }, + { + "left_line": 810, + "right_line": 810 + }, + { + "left_line": 811, + "right_line": 811 + }, + { + "left_line": 812, + "right_line": 812 + }, + { + "left_line": 813, + "right_line": 813 + }, + { + "left_line": 814, + "right_line": 814 + }, + { + "left_line": 815, + "right_line": 815 + }, + { + "left_line": 816, + "right_line": 816 + }, + { + "left_line": 817, + "right_line": 817 + }, + { + "left_line": 818, + "right_line": 818 + }, + { + "left_line": 819, + "right_line": 819 + }, + { + "left_line": 820, + "right_line": 820 + }, + { + "left_line": 821, + "right_line": 821 + }, + { + "left_line": 822, + "right_line": 822 + }, + { + "left_line": 823, + "right_line": 823 + }, + { + "left_line": 824, + "right_line": 824 + }, + { + "left_line": 825, + "right_line": 825 + }, + { + "left_line": 826, + "right_line": 826 + }, + { + "left_line": 827, + "right_line": 827 + }, + { + "left_line": 828, + "right_line": 828 + }, + { + "left_line": 829, + "right_line": 829 + }, + { + "left_line": 830, + "right_line": 830 + }, + { + "left_line": 831, + "right_line": 831 + }, + { + "left_line": 832, + "right_line": 832 + }, + { + "left_line": 833, + "right_line": 833 + }, + { + "left_line": 834, + "right_line": 834 + }, + { + "left_line": 835, + "right_line": 835 + }, + { + "left_line": 836, + "right_line": 836 + }, + { + "left_line": 837, + "right_line": 837 + }, + { + "left_line": 838, + "right_line": 838 + }, + { + "left_line": 839, + "right_line": 839 + }, + { + "left_line": 840, + "right_line": 840 + }, + { + "left_line": 841, + "right_line": 841 + }, + { + "left_line": 842, + "right_line": 842 + }, + { + "left_line": 843, + "right_line": 843 + }, + { + "left_line": 844, + "right_line": 844 + }, + { + "left_line": 845, + "right_line": 845 + }, + { + "left_line": 846, + "right_line": 846 + }, + { + "left_line": 847, + "right_line": 847 + }, + { + "left_line": 848, + "right_line": 848 + }, + { + "left_line": 849, + "right_line": 849 + }, + { + "left_line": 850, + "right_line": 850 + }, + { + "left_line": 851, + "right_line": 851 + }, + { + "left_line": 852, + "right_line": 852 + }, + { + "left_line": 853, + "right_line": 853 + }, + { + "left_line": 854, + "right_line": 854 + }, + { + "left_line": 855, + "right_line": 855 + }, + { + "left_line": 856, + "right_line": 856 + }, + { + "left_line": 857, + "right_line": 857 + }, + { + "left_line": 858, + "right_line": 858 + }, + { + "left_line": 859, + "right_line": 859 + }, + { + "left_line": 860, + "right_line": 860 + }, + { + "left_line": 861, + "right_line": 861 + }, + { + "left_line": 862, + "right_line": 862 + }, + { + "left_line": 863, + "right_line": 863 + }, + { + "left_line": 864, + "right_line": 864 + }, + { + "left_line": 865, + "right_line": 865 + }, + { + "left_line": 866, + "right_line": 866 + }, + { + "left_line": 867, + "right_line": 867 + }, + { + "left_line": 868, + "right_line": 868 + }, + { + "left_line": 869, + "right_line": 869 + }, + { + "left_line": 870, + "right_line": 870 + }, + { + "left_line": 871, + "right_line": 871 + }, + { + "left_line": 872, + "right_line": 872 + }, + { + "left_line": 873, + "right_line": 873 + }, + { + "left_line": 874, + "right_line": 874 + }, + { + "left_line": 875, + "right_line": 875 + }, + { + "left_line": 876, + "right_line": 876 + }, + { + "left_line": 877, + "right_line": 877 + }, + { + "left_line": 878, + "right_line": 878 + }, + { + "left_line": 879, + "right_line": 879 + }, + { + "left_line": 880, + "right_line": 880 + }, + { + "left_line": 881, + "right_line": 881 + }, + { + "left_line": 882, + "right_line": 882 + }, + { + "left_line": 883, + "right_line": 883 + }, + { + "left_line": 884, + "right_line": 884 + }, + { + "left_line": 885, + "right_line": 885 + }, + { + "left_line": 886, + "right_line": 886 + }, + { + "left_line": 887, + "right_line": 887 + }, + { + "left_line": 888, + "right_line": 888 + }, + { + "left_line": 889, + "right_line": 889 + }, + { + "left_line": 890, + "right_line": 890 + }, + { + "left_line": 891, + "right_line": 891 + }, + { + "left_line": 892, + "right_line": 892 + }, + { + "left_line": 893, + "right_line": 893 + }, + { + "left_line": 894, + "right_line": 894 + }, + { + "left_line": 895, + "right_line": 895 + }, + { + "left_line": 896, + "right_line": 896 + }, + { + "left_line": 897, + "right_line": 897 + }, + { + "left_line": 898, + "right_line": 898 + }, + { + "left_line": 899, + "right_line": 899 + }, + { + "left_line": 900, + "right_line": 900 + }, + { + "left_line": 901, + "right_line": 901 + }, + { + "left_line": 902, + "right_line": 902 + }, + { + "left_line": 903, + "right_line": 903 + }, + { + "left_line": 904, + "right_line": 904 + }, + { + "left_line": 905, + "right_line": 905 + }, + { + "left_line": 906, + "right_line": 906 + }, + { + "left_line": 907, + "right_line": 907 + }, + { + "left_line": 908, + "right_line": 908 + }, + { + "left_line": 909, + "right_line": 909 + }, + { + "left_line": 910, + "right_line": 910 + }, + { + "left_line": 911, + "right_line": 911 + }, + { + "left_line": 912, + "right_line": 912 + }, + { + "left_line": 913, + "right_line": 913 + }, + { + "left_line": 914, + "right_line": 914 + }, + { + "left_line": 915, + "right_line": 915 + }, + { + "left_line": 916, + "right_line": 916 + }, + { + "left_line": 917, + "right_line": 917 + }, + { + "left_line": 918, + "right_line": 918 + }, + { + "left_line": 919, + "right_line": 919 + }, + { + "left_line": 920, + "right_line": 920 + }, + { + "left_line": 921, + "right_line": 921 + }, + { + "left_line": 922, + "right_line": 922 + }, + { + "left_line": 923, + "right_line": 923 + }, + { + "left_line": 924, + "right_line": 924 + }, + { + "left_line": 925, + "right_line": 925 + }, + { + "left_line": 926, + "right_line": 926 + }, + { + "left_line": 927, + "right_line": 927 + }, + { + "left_line": 928, + "right_line": 928 + }, + { + "left_line": 929, + "right_line": 929 + }, + { + "left_line": 930, + "right_line": 930 + }, + { + "left_line": 931, + "right_line": 931 + }, + { + "left_line": 932, + "right_line": 932 + }, + { + "left_line": 933, + "right_line": 933 + }, + { + "left_line": 934, + "right_line": 934 + }, + { + "left_line": 935, + "right_line": 935 + }, + { + "left_line": 936, + "right_line": 936 + }, + { + "left_line": 937, + "right_line": 937 + }, + { + "left_line": 938, + "right_line": 938 + }, + { + "left_line": 939, + "right_line": 939 + }, + { + "left_line": 940, + "right_line": 940 + }, + { + "left_line": 941, + "right_line": 941 + }, + { + "left_line": 942, + "right_line": 942 + }, + { + "left_line": 943, + "right_line": 943 + }, + { + "left_line": 944, + "right_line": 944 + }, + { + "left_line": 945, + "right_line": 945 + }, + { + "left_line": 946, + "right_line": 946 + }, + { + "left_line": 947, + "right_line": 947 + }, + { + "left_line": 948, + "right_line": 948 + }, + { + "left_line": 949, + "right_line": 949 + }, + { + "left_line": 950, + "right_line": 950 + }, + { + "left_line": 951, + "right_line": 951 + }, + { + "left_line": 952, + "right_line": 952 + }, + { + "left_line": 953, + "right_line": 953 + }, + { + "left_line": 954, + "right_line": 954 + }, + { + "left_line": 955, + "right_line": 955 + }, + { + "left_line": 956, + "right_line": 956 + }, + { + "left_line": 957, + "right_line": 957 + }, + { + "left_line": 958, + "right_line": 958 + }, + { + "left_line": 959, + "right_line": 959 + }, + { + "left_line": 960, + "right_line": 960 + }, + { + "left_line": 961, + "right_line": 961 + }, + { + "left_line": 962, + "right_line": 962 + }, + { + "left_line": 963, + "right_line": 963 + }, + { + "left_line": 964, + "right_line": 964 + }, + { + "left_line": 965, + "right_line": 965 + }, + { + "left_line": 966, + "right_line": 966 + }, + { + "left_line": 967, + "right_line": 967 + }, + { + "left_line": 968, + "right_line": 968 + }, + { + "left_line": 969, + "right_line": 969 + }, + { + "left_line": 970, + "right_line": 970 + }, + { + "left_line": 971, + "right_line": 971 + }, + { + "left_line": 972, + "right_line": 972 + }, + { + "left_line": 973, + "right_line": 973 + }, + { + "left_line": 974, + "right_line": 974 + }, + { + "left_line": 975, + "right_line": 975 + }, + { + "left_line": 976, + "right_line": 976 + }, + { + "left_line": 977, + "right_line": 977 + }, + { + "left_line": 978, + "right_line": 978 + }, + { + "left_line": 979, + "right_line": 979 + }, + { + "left_line": 980, + "right_line": 980 + }, + { + "left_line": 981, + "right_line": 981 + }, + { + "left_line": 982, + "right_line": 982 + }, + { + "left_line": 983, + "right_line": 983 + }, + { + "left_line": 984, + "right_line": 984 + }, + { + "left_line": 985, + "right_line": 985 + }, + { + "left_line": 986, + "right_line": 986 + }, + { + "left_line": 987, + "right_line": 987 + }, + { + "left_line": 988, + "right_line": 988 + }, + { + "left_line": 989, + "right_line": 989 + }, + { + "left_line": 990, + "right_line": 990 + }, + { + "left_line": 991, + "right_line": 991 + }, + { + "left_line": 992, + "right_line": 992 + }, + { + "left_line": 993, + "right_line": 993 + }, + { + "left_line": 994, + "right_line": 994 + }, + { + "left_line": 995, + "right_line": 995 + }, + { + "left_line": 996, + "right_line": 996 + }, + { + "left_line": 997, + "right_line": 997 + }, + { + "left_line": 998, + "right_line": 998 + }, + { + "left_line": 999, + "right_line": 999 + }, + { + "left_line": 1000, + "right_line": 1000 + }, + { + "left_line": 1001, + "right_line": 1001 + }, + { + "left_line": 1002, + "right_line": 1002 + }, + { + "left_line": 1003, + "right_line": 1003 + }, + { + "left_line": 1004, + "right_line": 1004 + }, + { + "left_line": 1005, + "right_line": 1005 + }, + { + "left_line": 1006, + "right_line": 1006 + }, + { + "left_line": 1007, + "right_line": 1007 + }, + { + "left_line": 1008, + "right_line": 1008 + }, + { + "left_line": 1009, + "right_line": 1009 + }, + { + "left_line": 1010, + "right_line": 1010 + }, + { + "left_line": 1011, + "right_line": 1011 + }, + { + "left_line": 1012, + "right_line": 1012 + }, + { + "left_line": 1013, + "right_line": 1013 + }, + { + "left_line": 1014, + "right_line": 1014 + }, + { + "left_line": 1015, + "right_line": 1015 + }, + { + "left_line": 1016, + "right_line": 1016 + }, + { + "left_line": 1017, + "right_line": 1017 + }, + { + "left_line": 1018, + "right_line": 1018 + }, + { + "left_line": 1019, + "right_line": 1019 + }, + { + "left_line": 1020, + "right_line": 1020 + }, + { + "left_line": 1021, + "right_line": 1021 + }, + { + "left_line": 1022, + "right_line": 1022 + }, + { + "left_line": 1023, + "right_line": 1023 + }, + { + "left_line": 1024, + "right_line": 1024 + }, + { + "left_line": 1025, + "right_line": 1025 + }, + { + "left_line": 1026, + "right_line": 1026 + }, + { + "left_line": 1027, + "right_line": 1027 + }, + { + "left_line": 1028, + "right_line": 1028 + }, + { + "left_line": 1029, + "right_line": 1029 + }, + { + "left_line": 1030, + "right_line": 1030 + }, + { + "left_line": 1031, + "right_line": 1031 + }, + { + "left_line": 1032, + "right_line": 1032 + }, + { + "left_line": 1033, + "right_line": 1033 + }, + { + "left_line": 1034, + "right_line": 1034 + }, + { + "left_line": 1035, + "right_line": 1035 + }, + { + "left_line": 1036, + "right_line": 1036 + }, + { + "left_line": 1037, + "right_line": 1037 + }, + { + "left_line": 1038, + "right_line": 1038 + }, + { + "left_line": 1039, + "right_line": 1039 + }, + { + "left_line": 1040, + "right_line": 1040 + }, + { + "left_line": 1041, + "right_line": 1041 + }, + { + "left_line": 1042, + "right_line": 1042 + }, + { + "left_line": 1043, + "right_line": 1043 + }, + { + "left_line": 1044, + "right_line": 1044 + }, + { + "left_line": 1045, + "right_line": 1045 + }, + { + "left_line": 1046, + "right_line": 1046 + }, + { + "left_line": 1047, + "right_line": 1047 + }, + { + "left_line": 1048, + "right_line": 1048 + }, + { + "left_line": 1049, + "right_line": 1049 + }, + { + "left_line": 1050, + "right_line": 1050 + }, + { + "left_line": 1051, + "right_line": 1051 + }, + { + "left_line": 1052, + "right_line": 1052 + }, + { + "left_line": 1053, + "right_line": 1053 + }, + { + "left_line": 1054, + "right_line": 1054 + }, + { + "left_line": 1055, + "right_line": 1055 + }, + { + "left_line": 1056, + "right_line": 1056 + }, + { + "left_line": 1057, + "right_line": 1057 + }, + { + "left_line": 1058, + "right_line": 1058 + }, + { + "left_line": 1059, + "right_line": 1059 + }, + { + "left_line": 1060, + "right_line": 1060 + }, + { + "left_line": 1061, + "right_line": 1061 + }, + { + "left_line": 1062, + "right_line": 1062 + }, + { + "left_line": 1063, + "right_line": 1063 + }, + { + "left_line": 1064, + "right_line": 1064 + }, + { + "left_line": 1065, + "right_line": 1065 + }, + { + "left_line": 1066, + "right_line": 1066 + }, + { + "left_line": 1067, + "right_line": 1067 + }, + { + "left_line": 1068, + "right_line": 1068 + }, + { + "left_line": 1069, + "right_line": 1069 + }, + { + "left_line": 1070, + "right_line": 1070 + }, + { + "left_line": 1071, + "right_line": 1071 + }, + { + "left_line": 1072, + "right_line": 1072 + }, + { + "left_line": 1073, + "right_line": 1073 + }, + { + "left_line": 1074, + "right_line": 1074 + }, + { + "left_line": 1075, + "right_line": 1075 + }, + { + "left_line": 1076, + "right_line": 1076 + }, + { + "left_line": 1077, + "right_line": 1077 + }, + { + "left_line": 1078, + "right_line": 1078 + }, + { + "left_line": 1079, + "right_line": 1079 + }, + { + "left_line": 1080, + "right_line": 1080 + }, + { + "left_line": 1081, + "right_line": 1081 + }, + { + "left_line": 1082, + "right_line": 1082 + }, + { + "left_line": 1083, + "right_line": 1083 + }, + { + "left_line": 1084, + "right_line": 1084 + }, + { + "left_line": 1085, + "right_line": 1085 + }, + { + "left_line": 1086, + "right_line": 1086 + }, + { + "left_line": 1087, + "right_line": 1087 + }, + { + "left_line": 1088, + "right_line": 1088 + }, + { + "left_line": 1089, + "right_line": 1089 + }, + { + "left_line": 1090, + "right_line": 1090 + }, + { + "left_line": 1091, + "right_line": 1091 + }, + { + "left_line": 1092, + "right_line": 1092 + }, + { + "left_line": 1093, + "right_line": 1093 + }, + { + "left_line": 1094, + "right_line": 1094 + }, + { + "left_line": 1095, + "right_line": -1 + }, + { + "left_line": -1, + "right_line": 1095 + }, + { + "left_line": -1, + "right_line": 1096 + }, + { + "left_line": -1, + "right_line": 1097 + }, + { + "left_line": -1, + "right_line": 1098 + }, + { + "left_line": -1, + "right_line": 1099 + }, + { + "left_line": -1, + "right_line": 1100 + }, + { + "left_line": -1, + "right_line": 1101 + }, + { + "left_line": 1096, + "right_line": 1102 + }, + { + "left_line": 1097, + "right_line": 1103 + }, + { + "left_line": 1098, + "right_line": 1104 + }, + { + "left_line": 1099, + "right_line": 1105 + }, + { + "left_line": 1100, + "right_line": 1106 + }, + { + "left_line": 1101, + "right_line": 1107 + }, + { + "left_line": 1102, + "right_line": 1108 + }, + { + "left_line": 1103, + "right_line": 1109 + }, + { + "left_line": 1104, + "right_line": 1110 + }, + { + "left_line": 1105, + "right_line": 1111 + }, + { + "left_line": 1106, + "right_line": 1112 + }, + { + "left_line": 1107, + "right_line": 1113 + }, + { + "left_line": 1108, + "right_line": 1114 + }, + { + "left_line": 1109, + "right_line": 1115 + }, + { + "left_line": 1110, + "right_line": 1116 + }, + { + "left_line": 1111, + "right_line": 1117 + }, + { + "left_line": 1112, + "right_line": 1118 + }, + { + "left_line": 1113, + "right_line": 1119 + }, + { + "left_line": 1114, + "right_line": 1120 + }, + { + "left_line": 1115, + "right_line": 1121 + }, + { + "left_line": 1116, + "right_line": 1122 + }, + { + "left_line": 1117, + "right_line": 1123 + }, + { + "left_line": 1118, + "right_line": 1124 + }, + { + "left_line": 1119, + "right_line": 1125 + }, + { + "left_line": 1120, + "right_line": 1126 + }, + { + "left_line": 1121, + "right_line": 1127 + }, + { + "left_line": 1122, + "right_line": 1128 + }, + { + "left_line": 1123, + "right_line": 1129 + }, + { + "left_line": 1124, + "right_line": 1130 + }, + { + "left_line": 1125, + "right_line": 1131 + }, + { + "left_line": 1126, + "right_line": 1132 + }, + { + "left_line": 1127, + "right_line": 1133 + }, + { + "left_line": 1128, + "right_line": 1134 + }, + { + "left_line": 1129, + "right_line": 1135 + }, + { + "left_line": 1130, + "right_line": 1136 + }, + { + "left_line": 1131, + "right_line": 1137 + }, + { + "left_line": 1132, + "right_line": 1138 + }, + { + "left_line": 1133, + "right_line": 1139 + }, + { + "left_line": 1134, + "right_line": 1140 + }, + { + "left_line": 1135, + "right_line": 1141 + }, + { + "left_line": 1136, + "right_line": 1142 + }, + { + "left_line": 1137, + "right_line": 1143 + }, + { + "left_line": 1138, + "right_line": 1144 + }, + { + "left_line": 1139, + "right_line": 1145 + }, + { + "left_line": 1140, + "right_line": 1146 + }, + { + "left_line": 1141, + "right_line": 1147 + }, + { + "left_line": 1142, + "right_line": 1148 + }, + { + "left_line": 1143, + "right_line": 1149 + }, + { + "left_line": 1144, + "right_line": 1150 + }, + { + "left_line": 1145, + "right_line": 1151 + }, + { + "left_line": 1146, + "right_line": 1152 + }, + { + "left_line": 1147, + "right_line": 1153 + }, + { + "left_line": 1148, + "right_line": 1154 + }, + { + "left_line": 1149, + "right_line": 1155 + }, + { + "left_line": 1150, + "right_line": 1156 + }, + { + "left_line": 1151, + "right_line": 1157 + }, + { + "left_line": 1152, + "right_line": 1158 + }, + { + "left_line": 1153, + "right_line": 1159 + }, + { + "left_line": 1154, + "right_line": 1160 + }, + { + "left_line": 1155, + "right_line": 1161 + }, + { + "left_line": 1156, + "right_line": 1162 + }, + { + "left_line": 1157, + "right_line": 1163 + }, + { + "left_line": 1158, + "right_line": 1164 + }, + { + "left_line": 1159, + "right_line": 1165 + }, + { + "left_line": 1160, + "right_line": 1166 + }, + { + "left_line": 1161, + "right_line": 1167 + }, + { + "left_line": 1162, + "right_line": 1168 + }, + { + "left_line": 1163, + "right_line": 1169 + }, + { + "left_line": 1164, + "right_line": 1170 + }, + { + "left_line": 1165, + "right_line": 1171 + }, + { + "left_line": 1166, + "right_line": 1172 + }, + { + "left_line": 1167, + "right_line": 1173 + }, + { + "left_line": 1168, + "right_line": 1174 + }, + { + "left_line": 1169, + "right_line": 1175 + }, + { + "left_line": 1170, + "right_line": 1176 + }, + { + "left_line": 1171, + "right_line": 1177 + }, + { + "left_line": 1172, + "right_line": 1178 + }, + { + "left_line": 1173, + "right_line": 1179 + }, + { + "left_line": 1174, + "right_line": 1180 + }, + { + "left_line": 1175, + "right_line": 1181 + }, + { + "left_line": 1176, + "right_line": 1182 + }, + { + "left_line": 1177, + "right_line": 1183 + }, + { + "left_line": 1178, + "right_line": 1184 + }, + { + "left_line": 1179, + "right_line": 1185 + }, + { + "left_line": 1180, + "right_line": 1186 + }, + { + "left_line": 1181, + "right_line": 1187 + }, + { + "left_line": 1182, + "right_line": 1188 + }, + { + "left_line": 1183, + "right_line": 1189 + }, + { + "left_line": 1184, + "right_line": 1190 + }, + { + "left_line": 1185, + "right_line": 1191 + }, + { + "left_line": 1186, + "right_line": 1192 + }, + { + "left_line": 1187, + "right_line": 1193 + }, + { + "left_line": 1188, + "right_line": 1194 + }, + { + "left_line": 1189, + "right_line": 1195 + }, + { + "left_line": 1190, + "right_line": 1196 + }, + { + "left_line": 1191, + "right_line": 1197 + }, + { + "left_line": 1192, + "right_line": 1198 + }, + { + "left_line": 1193, + "right_line": 1199 + }, + { + "left_line": 1194, + "right_line": 1200 + }, + { + "left_line": 1195, + "right_line": 1201 + }, + { + "left_line": 1196, + "right_line": 1202 + }, + { + "left_line": 1197, + "right_line": 1203 + }, + { + "left_line": 1198, + "right_line": 1204 + }, + { + "left_line": 1199, + "right_line": 1205 + }, + { + "left_line": 1200, + "right_line": 1206 + }, + { + "left_line": 1201, + "right_line": 1207 + }, + { + "left_line": 1202, + "right_line": 1208 + }, + { + "left_line": 1203, + "right_line": 1209 + }, + { + "left_line": 1204, + "right_line": 1210 + }, + { + "left_line": 1205, + "right_line": 1211 + }, + { + "left_line": 1206, + "right_line": 1212 + }, + { + "left_line": 1207, + "right_line": 1213 + }, + { + "left_line": 1208, + "right_line": 1214 + }, + { + "left_line": 1209, + "right_line": 1215 + }, + { + "left_line": 1210, + "right_line": 1216 + }, + { + "left_line": 1211, + "right_line": 1217 + }, + { + "left_line": 1212, + "right_line": 1218 + }, + { + "left_line": 1213, + "right_line": 1219 + }, + { + "left_line": 1214, + "right_line": 1220 + }, + { + "left_line": 1215, + "right_line": 1221 + }, + { + "left_line": 1216, + "right_line": 1222 + }, + { + "left_line": 1217, + "right_line": 1223 + }, + { + "left_line": 1218, + "right_line": 1224 + }, + { + "left_line": 1219, + "right_line": 1225 + }, + { + "left_line": 1220, + "right_line": 1226 + }, + { + "left_line": 1221, + "right_line": 1227 + }, + { + "left_line": 1222, + "right_line": 1228 + }, + { + "left_line": 1223, + "right_line": 1229 + }, + { + "left_line": 1224, + "right_line": 1230 + }, + { + "left_line": 1225, + "right_line": 1231 + }, + { + "left_line": 1226, + "right_line": 1232 + }, + { + "left_line": 1227, + "right_line": 1233 + }, + { + "left_line": 1228, + "right_line": 1234 + }, + { + "left_line": 1229, + "right_line": 1235 + }, + { + "left_line": 1230, + "right_line": 1236 + }, + { + "left_line": 1231, + "right_line": 1237 + }, + { + "left_line": 1232, + "right_line": 1238 + }, + { + "left_line": 1233, + "right_line": 1239 + }, + { + "left_line": 1234, + "right_line": 1240 + }, + { + "left_line": 1235, + "right_line": 1241 + }, + { + "left_line": 1236, + "right_line": 1242 + }, + { + "left_line": 1237, + "right_line": 1243 + }, + { + "left_line": 1238, + "right_line": 1244 + }, + { + "left_line": 1239, + "right_line": 1245 + }, + { + "left_line": 1240, + "right_line": 1246 + }, + { + "left_line": 1241, + "right_line": 1247 + }, + { + "left_line": 1242, + "right_line": 1248 + }, + { + "left_line": 1243, + "right_line": 1249 + }, + { + "left_line": 1244, + "right_line": 1250 + }, + { + "left_line": 1245, + "right_line": 1251 + }, + { + "left_line": 1246, + "right_line": 1252 + }, + { + "left_line": 1247, + "right_line": 1253 + }, + { + "left_line": 1248, + "right_line": 1254 + }, + { + "left_line": 1249, + "right_line": 1255 + }, + { + "left_line": 1250, + "right_line": 1256 + }, + { + "left_line": 1251, + "right_line": 1257 + }, + { + "left_line": 1252, + "right_line": 1258 + }, + { + "left_line": 1253, + "right_line": 1259 + }, + { + "left_line": 1254, + "right_line": 1260 + }, + { + "left_line": 1255, + "right_line": 1261 + }, + { + "left_line": 1256, + "right_line": 1262 + }, + { + "left_line": 1257, + "right_line": 1263 + }, + { + "left_line": 1258, + "right_line": 1264 + }, + { + "left_line": 1259, + "right_line": 1265 + }, + { + "left_line": 1260, + "right_line": 1266 + }, + { + "left_line": 1261, + "right_line": 1267 + }, + { + "left_line": 1262, + "right_line": 1268 + }, + { + "left_line": 1263, + "right_line": 1269 + }, + { + "left_line": 1264, + "right_line": 1270 + }, + { + "left_line": 1265, + "right_line": 1271 + }, + { + "left_line": 1266, + "right_line": 1272 + }, + { + "left_line": 1267, + "right_line": 1273 + }, + { + "left_line": 1268, + "right_line": 1274 + }, + { + "left_line": 1269, + "right_line": 1275 + }, + { + "left_line": 1270, + "right_line": 1276 + }, + { + "left_line": 1271, + "right_line": 1277 + }, + { + "left_line": 1272, + "right_line": 1278 + }, + { + "left_line": 1273, + "right_line": 1279 + }, + { + "left_line": 1274, + "right_line": 1280 + }, + { + "left_line": 1275, + "right_line": 1281 + }, + { + "left_line": 1276, + "right_line": 1282 + }, + { + "left_line": 1277, + "right_line": 1283 + }, + { + "left_line": 1278, + "right_line": 1284 + }, + { + "left_line": 1279, + "right_line": 1285 + }, + { + "left_line": 1280, + "right_line": 1286 + }, + { + "left_line": 1281, + "right_line": 1287 + }, + { + "left_line": 1282, + "right_line": 1288 + }, + { + "left_line": 1283, + "right_line": 1289 + }, + { + "left_line": 1284, + "right_line": 1290 + }, + { + "left_line": 1285, + "right_line": 1291 + }, + { + "left_line": 1286, + "right_line": 1292 + }, + { + "left_line": 1287, + "right_line": 1293 + }, + { + "left_line": 1288, + "right_line": 1294 + }, + { + "left_line": 1289, + "right_line": 1295 + }, + { + "left_line": 1290, + "right_line": 1296 + }, + { + "left_line": 1291, + "right_line": 1297 + }, + { + "left_line": 1292, + "right_line": 1298 + }, + { + "left_line": 1293, + "right_line": 1299 + }, + { + "left_line": 1294, + "right_line": 1300 + }, + { + "left_line": 1295, + "right_line": 1301 + }, + { + "left_line": 1296, + "right_line": 1302 + }, + { + "left_line": 1297, + "right_line": 1303 + }, + { + "left_line": 1298, + "right_line": 1304 + }, + { + "left_line": 1299, + "right_line": 1305 + }, + { + "left_line": 1300, + "right_line": 1306 + }, + { + "left_line": 1301, + "right_line": 1307 + }, + { + "left_line": 1302, + "right_line": 1308 + }, + { + "left_line": 1303, + "right_line": 1309 + }, + { + "left_line": 1304, + "right_line": 1310 + }, + { + "left_line": 1305, + "right_line": 1311 + }, + { + "left_line": 1306, + "right_line": 1312 + }, + { + "left_line": 1307, + "right_line": 1313 + }, + { + "left_line": 1308, + "right_line": 1314 + }, + { + "left_line": 1309, + "right_line": 1315 + }, + { + "left_line": 1310, + "right_line": 1316 + }, + { + "left_line": 1311, + "right_line": 1317 + }, + { + "left_line": 1312, + "right_line": 1318 + }, + { + "left_line": 1313, + "right_line": 1319 + }, + { + "left_line": 1314, + "right_line": 1320 + }, + { + "left_line": 1315, + "right_line": 1321 + }, + { + "left_line": 1316, + "right_line": 1322 + }, + { + "left_line": 1317, + "right_line": 1323 + }, + { + "left_line": 1318, + "right_line": 1324 + }, + { + "left_line": 1319, + "right_line": 1325 + }, + { + "left_line": 1320, + "right_line": 1326 + }, + { + "left_line": 1321, + "right_line": 1327 + }, + { + "left_line": 1322, + "right_line": 1328 + }, + { + "left_line": 1323, + "right_line": 1329 + }, + { + "left_line": 1324, + "right_line": 1330 + }, + { + "left_line": 1325, + "right_line": 1331 + }, + { + "left_line": 1326, + "right_line": 1332 + }, + { + "left_line": 1327, + "right_line": 1333 + }, + { + "left_line": 1328, + "right_line": 1334 + }, + { + "left_line": 1329, + "right_line": 1335 + }, + { + "left_line": 1330, + "right_line": 1336 + }, + { + "left_line": 1331, + "right_line": 1337 + }, + { + "left_line": 1332, + "right_line": 1338 + }, + { + "left_line": 1333, + "right_line": 1339 + }, + { + "left_line": 1334, + "right_line": 1340 + }, + { + "left_line": 1335, + "right_line": 1341 + }, + { + "left_line": 1336, + "right_line": 1342 + }, + { + "left_line": 1337, + "right_line": 1343 + }, + { + "left_line": 1338, + "right_line": 1344 + }, + { + "left_line": 1339, + "right_line": 1345 + }, + { + "left_line": 1340, + "right_line": 1346 + }, + { + "left_line": 1341, + "right_line": 1347 + }, + { + "left_line": 1342, + "right_line": 1348 + }, + { + "left_line": 1343, + "right_line": 1349 + }, + { + "left_line": 1344, + "right_line": 1350 + }, + { + "left_line": 1345, + "right_line": 1351 + }, + { + "left_line": 1346, + "right_line": 1352 + }, + { + "left_line": 1347, + "right_line": 1353 + }, + { + "left_line": 1348, + "right_line": 1354 + }, + { + "left_line": 1349, + "right_line": 1355 + }, + { + "left_line": 1350, + "right_line": 1356 + }, + { + "left_line": 1351, + "right_line": 1357 + }, + { + "left_line": 1352, + "right_line": 1358 + }, + { + "left_line": 1353, + "right_line": 1359 + }, + { + "left_line": 1354, + "right_line": 1360 + }, + { + "left_line": 1355, + "right_line": 1361 + }, + { + "left_line": 1356, + "right_line": 1362 + }, + { + "left_line": 1357, + "right_line": 1363 + }, + { + "left_line": 1358, + "right_line": 1364 + }, + { + "left_line": 1359, + "right_line": 1365 + }, + { + "left_line": 1360, + "right_line": 1366 + }, + { + "left_line": 1361, + "right_line": 1367 + }, + { + "left_line": 1362, + "right_line": 1368 + }, + { + "left_line": 1363, + "right_line": 1369 + }, + { + "left_line": 1364, + "right_line": 1370 + }, + { + "left_line": 1365, + "right_line": 1371 + }, + { + "left_line": 1366, + "right_line": 1372 + }, + { + "left_line": 1367, + "right_line": 1373 + }, + { + "left_line": 1368, + "right_line": 1374 + }, + { + "left_line": 1369, + "right_line": 1375 + }, + { + "left_line": 1370, + "right_line": 1376 + }, + { + "left_line": 1371, + "right_line": 1377 + }, + { + "left_line": 1372, + "right_line": 1378 + }, + { + "left_line": 1373, + "right_line": 1379 + }, + { + "left_line": 1374, + "right_line": 1380 + }, + { + "left_line": 1375, + "right_line": 1381 + }, + { + "left_line": 1376, + "right_line": 1382 + }, + { + "left_line": 1377, + "right_line": 1383 + }, + { + "left_line": 1378, + "right_line": 1384 + }, + { + "left_line": 1379, + "right_line": 1385 + }, + { + "left_line": 1380, + "right_line": 1386 + }, + { + "left_line": 1381, + "right_line": 1387 + }, + { + "left_line": 1382, + "right_line": 1388 + }, + { + "left_line": 1383, + "right_line": 1389 + }, + { + "left_line": 1384, + "right_line": 1390 + }, + { + "left_line": 1385, + "right_line": 1391 + }, + { + "left_line": 1386, + "right_line": 1392 + }, + { + "left_line": 1387, + "right_line": 1393 + }, + { + "left_line": 1388, + "right_line": 1394 + }, + { + "left_line": 1389, + "right_line": 1395 + }, + { + "left_line": 1390, + "right_line": 1396 + }, + { + "left_line": 1391, + "right_line": 1397 + }, + { + "left_line": 1392, + "right_line": 1398 + }, + { + "left_line": 1393, + "right_line": 1399 + }, + { + "left_line": 1394, + "right_line": 1400 + }, + { + "left_line": 1395, + "right_line": 1401 + }, + { + "left_line": 1396, + "right_line": 1402 + }, + { + "left_line": 1397, + "right_line": 1403 + }, + { + "left_line": 1398, + "right_line": 1404 + }, + { + "left_line": 1399, + "right_line": 1405 + }, + { + "left_line": 1400, + "right_line": 1406 + }, + { + "left_line": 1401, + "right_line": 1407 + }, + { + "left_line": 1402, + "right_line": 1408 + }, + { + "left_line": 1403, + "right_line": 1409 + }, + { + "left_line": 1404, + "right_line": 1410 + }, + { + "left_line": 1405, + "right_line": 1411 + }, + { + "left_line": 1406, + "right_line": 1412 + }, + { + "left_line": 1407, + "right_line": 1413 + }, + { + "left_line": 1408, + "right_line": 1414 + }, + { + "left_line": 1409, + "right_line": 1415 + }, + { + "left_line": 1410, + "right_line": 1416 + }, + { + "left_line": 1411, + "right_line": 1417 + }, + { + "left_line": 1412, + "right_line": 1418 + }, + { + "left_line": 1413, + "right_line": 1419 + }, + { + "left_line": 1414, + "right_line": 1420 + }, + { + "left_line": 1415, + "right_line": 1421 + }, + { + "left_line": 1416, + "right_line": 1422 + }, + { + "left_line": 1417, + "right_line": 1423 + }, + { + "left_line": 1418, + "right_line": 1424 + }, + { + "left_line": 1419, + "right_line": 1425 + }, + { + "left_line": 1420, + "right_line": 1426 + }, + { + "left_line": 1421, + "right_line": 1427 + }, + { + "left_line": 1422, + "right_line": 1428 + }, + { + "left_line": 1423, + "right_line": 1429 + }, + { + "left_line": 1424, + "right_line": 1430 + }, + { + "left_line": 1425, + "right_line": 1431 + }, + { + "left_line": 1426, + "right_line": 1432 + }, + { + "left_line": 1427, + "right_line": 1433 + }, + { + "left_line": 1428, + "right_line": 1434 + }, + { + "left_line": 1429, + "right_line": 1435 + }, + { + "left_line": 1430, + "right_line": 1436 + }, + { + "left_line": 1431, + "right_line": 1437 + }, + { + "left_line": 1432, + "right_line": 1438 + }, + { + "left_line": 1433, + "right_line": 1439 + }, + { + "left_line": 1434, + "right_line": 1440 + }, + { + "left_line": 1435, + "right_line": 1441 + }, + { + "left_line": 1436, + "right_line": 1442 + }, + { + "left_line": 1437, + "right_line": 1443 + }, + { + "left_line": 1438, + "right_line": 1444 + }, + { + "left_line": 1439, + "right_line": 1445 + }, + { + "left_line": 1440, + "right_line": 1446 + }, + { + "left_line": 1441, + "right_line": 1447 + }, + { + "left_line": 1442, + "right_line": 1448 + }, + { + "left_line": 1443, + "right_line": 1449 + }, + { + "left_line": 1444, + "right_line": 1450 + }, + { + "left_line": 1445, + "right_line": 1451 + }, + { + "left_line": 1446, + "right_line": 1452 + }, + { + "left_line": 1447, + "right_line": 1453 + }, + { + "left_line": 1448, + "right_line": 1454 + }, + { + "left_line": 1449, + "right_line": 1455 + }, + { + "left_line": 1450, + "right_line": 1456 + }, + { + "left_line": 1451, + "right_line": 1457 + }, + { + "left_line": 1452, + "right_line": 1458 + }, + { + "left_line": 1453, + "right_line": 1459 + }, + { + "left_line": 1454, + "right_line": 1460 + }, + { + "left_line": 1455, + "right_line": 1461 + }, + { + "left_line": 1456, + "right_line": 1462 + }, + { + "left_line": 1457, + "right_line": 1463 + }, + { + "left_line": 1458, + "right_line": 1464 + }, + { + "left_line": 1459, + "right_line": 1465 + }, + { + "left_line": 1460, + "right_line": 1466 + }, + { + "left_line": 1461, + "right_line": 1467 + }, + { + "left_line": 1462, + "right_line": 1468 + }, + { + "left_line": 1463, + "right_line": 1469 + }, + { + "left_line": 1464, + "right_line": 1470 + }, + { + "left_line": 1465, + "right_line": 1471 + }, + { + "left_line": 1466, + "right_line": 1472 + }, + { + "left_line": 1467, + "right_line": 1473 + }, + { + "left_line": 1468, + "right_line": 1474 + }, + { + "left_line": 1469, + "right_line": 1475 + }, + { + "left_line": 1470, + "right_line": 1476 + }, + { + "left_line": 1471, + "right_line": 1477 + }, + { + "left_line": 1472, + "right_line": 1478 + }, + { + "left_line": 1473, + "right_line": 1479 + }, + { + "left_line": 1474, + "right_line": 1480 + }, + { + "left_line": 1475, + "right_line": 1481 + }, + { + "left_line": 1476, + "right_line": 1482 + }, + { + "left_line": 1477, + "right_line": 1483 + }, + { + "left_line": 1478, + "right_line": 1484 + }, + { + "left_line": 1479, + "right_line": 1485 + }, + { + "left_line": 1480, + "right_line": 1486 + }, + { + "left_line": 1481, + "right_line": 1487 + }, + { + "left_line": 1482, + "right_line": 1488 + }, + { + "left_line": 1483, + "right_line": 1489 + }, + { + "left_line": 1484, + "right_line": 1490 + }, + { + "left_line": 1485, + "right_line": 1491 + }, + { + "left_line": 1486, + "right_line": 1492 + }, + { + "left_line": 1487, + "right_line": 1493 + }, + { + "left_line": 1488, + "right_line": 1494 + }, + { + "left_line": 1489, + "right_line": 1495 + }, + { + "left_line": 1490, + "right_line": 1496 + }, + { + "left_line": 1491, + "right_line": 1497 + }, + { + "left_line": 1492, + "right_line": 1498 + }, + { + "left_line": 1493, + "right_line": 1499 + }, + { + "left_line": 1494, + "right_line": 1500 + }, + { + "left_line": 1495, + "right_line": 1501 + }, + { + "left_line": 1496, + "right_line": 1502 + }, + { + "left_line": 1497, + "right_line": 1503 + }, + { + "left_line": 1498, + "right_line": 1504 + }, + { + "left_line": 1499, + "right_line": 1505 + }, + { + "left_line": 1500, + "right_line": 1506 + }, + { + "left_line": 1501, + "right_line": 1507 + }, + { + "left_line": 1502, + "right_line": 1508 + }, + { + "left_line": 1503, + "right_line": 1509 + }, + { + "left_line": 1504, + "right_line": 1510 + }, + { + "left_line": 1505, + "right_line": 1511 + }, + { + "left_line": 1506, + "right_line": 1512 + }, + { + "left_line": 1507, + "right_line": 1513 + }, + { + "left_line": 1508, + "right_line": 1514 + }, + { + "left_line": 1509, + "right_line": 1515 + }, + { + "left_line": 1510, + "right_line": 1516 + }, + { + "left_line": 1511, + "right_line": 1517 + }, + { + "left_line": 1512, + "right_line": 1518 + }, + { + "left_line": 1513, + "right_line": 1519 + }, + { + "left_line": 1514, + "right_line": 1520 + }, + { + "left_line": 1515, + "right_line": 1521 + }, + { + "left_line": 1516, + "right_line": 1522 + }, + { + "left_line": 1517, + "right_line": 1523 + }, + { + "left_line": 1518, + "right_line": 1524 + }, + { + "left_line": 1519, + "right_line": 1525 + }, + { + "left_line": 1520, + "right_line": 1526 + }, + { + "left_line": 1521, + "right_line": 1527 + }, + { + "left_line": 1522, + "right_line": 1528 + }, + { + "left_line": 1523, + "right_line": 1529 + }, + { + "left_line": 1524, + "right_line": 1530 + }, + { + "left_line": 1525, + "right_line": 1531 + }, + { + "left_line": 1526, + "right_line": 1532 + }, + { + "left_line": 1527, + "right_line": 1533 + }, + { + "left_line": 1528, + "right_line": 1534 + }, + { + "left_line": 1529, + "right_line": 1535 + }, + { + "left_line": 1530, + "right_line": 1536 + }, + { + "left_line": 1531, + "right_line": 1537 + }, + { + "left_line": 1532, + "right_line": 1538 + }, + { + "left_line": 1533, + "right_line": 1539 + }, + { + "left_line": 1534, + "right_line": 1540 + }, + { + "left_line": 1535, + "right_line": 1541 + }, + { + "left_line": 1536, + "right_line": 1542 + }, + { + "left_line": 1537, + "right_line": 1543 + }, + { + "left_line": 1538, + "right_line": 1544 + }, + { + "left_line": 1539, + "right_line": 1545 + }, + { + "left_line": 1540, + "right_line": 1546 + }, + { + "left_line": 1541, + "right_line": 1547 + }, + { + "left_line": 1542, + "right_line": 1548 + }, + { + "left_line": 1543, + "right_line": 1549 + }, + { + "left_line": 1544, + "right_line": 1550 + }, + { + "left_line": 1545, + "right_line": 1551 + }, + { + "left_line": 1546, + "right_line": 1552 + }, + { + "left_line": 1547, + "right_line": 1553 + }, + { + "left_line": 1548, + "right_line": 1554 + }, + { + "left_line": 1549, + "right_line": 1555 + }, + { + "left_line": 1550, + "right_line": 1556 + }, + { + "left_line": 1551, + "right_line": 1557 + }, + { + "left_line": 1552, + "right_line": 1558 + }, + { + "left_line": 1553, + "right_line": 1559 + }, + { + "left_line": 1554, + "right_line": 1560 + }, + { + "left_line": 1555, + "right_line": 1561 + }, + { + "left_line": 1556, + "right_line": 1562 + }, + { + "left_line": 1557, + "right_line": 1563 + }, + { + "left_line": 1558, + "right_line": 1564 + }, + { + "left_line": 1559, + "right_line": 1565 + }, + { + "left_line": 1560, + "right_line": 1566 + }, + { + "left_line": 1561, + "right_line": 1567 + }, + { + "left_line": 1562, + "right_line": 1568 + }, + { + "left_line": 1563, + "right_line": 1569 + }, + { + "left_line": 1564, + "right_line": 1570 + }, + { + "left_line": 1565, + "right_line": 1571 + }, + { + "left_line": 1566, + "right_line": 1572 + }, + { + "left_line": 1567, + "right_line": 1573 + }, + { + "left_line": 1568, + "right_line": 1574 + }, + { + "left_line": 1569, + "right_line": 1575 + }, + { + "left_line": 1570, + "right_line": 1576 + }, + { + "left_line": 1571, + "right_line": 1577 + }, + { + "left_line": 1572, + "right_line": 1578 + }, + { + "left_line": 1573, + "right_line": 1579 + }, + { + "left_line": 1574, + "right_line": 1580 + }, + { + "left_line": 1575, + "right_line": 1581 + }, + { + "left_line": 1576, + "right_line": 1582 + }, + { + "left_line": 1577, + "right_line": 1583 + }, + { + "left_line": 1578, + "right_line": 1584 + }, + { + "left_line": 1579, + "right_line": 1585 + }, + { + "left_line": 1580, + "right_line": 1586 + }, + { + "left_line": 1581, + "right_line": 1587 + }, + { + "left_line": 1582, + "right_line": 1588 + }, + { + "left_line": 1583, + "right_line": 1589 + }, + { + "left_line": 1584, + "right_line": 1590 + }, + { + "left_line": 1585, + "right_line": 1591 + }, + { + "left_line": 1586, + "right_line": 1592 + }, + { + "left_line": 1587, + "right_line": 1593 + }, + { + "left_line": 1588, + "right_line": 1594 + }, + { + "left_line": 1589, + "right_line": 1595 + }, + { + "left_line": 1590, + "right_line": 1596 + }, + { + "left_line": 1591, + "right_line": 1597 + }, + { + "left_line": 1592, + "right_line": 1598 + }, + { + "left_line": 1593, + "right_line": 1599 + }, + { + "left_line": 1594, + "right_line": 1600 + }, + { + "left_line": 1595, + "right_line": 1601 + }, + { + "left_line": 1596, + "right_line": 1602 + }, + { + "left_line": 1597, + "right_line": 1603 + }, + { + "left_line": 1598, + "right_line": 1604 + }, + { + "left_line": 1599, + "right_line": 1605 + }, + { + "left_line": 1600, + "right_line": 1606 + }, + { + "left_line": 1601, + "right_line": 1607 + }, + { + "left_line": 1602, + "right_line": 1608 + }, + { + "left_line": 1603, + "right_line": 1609 + }, + { + "left_line": 1604, + "right_line": 1610 + }, + { + "left_line": 1605, + "right_line": 1611 + }, + { + "left_line": 1606, + "right_line": 1612 + }, + { + "left_line": 1607, + "right_line": 1613 + }, + { + "left_line": 1608, + "right_line": 1614 + }, + { + "left_line": 1609, + "right_line": 1615 + }, + { + "left_line": 1610, + "right_line": 1616 + }, + { + "left_line": 1611, + "right_line": 1617 + }, + { + "left_line": 1612, + "right_line": 1618 + }, + { + "left_line": 1613, + "right_line": 1619 + }, + { + "left_line": 1614, + "right_line": 1620 + }, + { + "left_line": 1615, + "right_line": 1621 + }, + { + "left_line": 1616, + "right_line": 1622 + }, + { + "left_line": 1617, + "right_line": 1623 + }, + { + "left_line": 1618, + "right_line": 1624 + }, + { + "left_line": 1619, + "right_line": 1625 + }, + { + "left_line": 1620, + "right_line": 1626 + }, + { + "left_line": 1621, + "right_line": 1627 + }, + { + "left_line": 1622, + "right_line": 1628 + }, + { + "left_line": 1623, + "right_line": 1629 + }, + { + "left_line": 1624, + "right_line": 1630 + }, + { + "left_line": 1625, + "right_line": 1631 + }, + { + "left_line": 1626, + "right_line": 1632 + }, + { + "left_line": 1627, + "right_line": 1633 + }, + { + "left_line": 1628, + "right_line": 1634 + }, + { + "left_line": 1629, + "right_line": 1635 + }, + { + "left_line": 1630, + "right_line": 1636 + }, + { + "left_line": 1631, + "right_line": 1637 + }, + { + "left_line": 1632, + "right_line": 1638 + }, + { + "left_line": 1633, + "right_line": 1639 + }, + { + "left_line": 1634, + "right_line": 1640 + }, + { + "left_line": 1635, + "right_line": 1641 + }, + { + "left_line": 1636, + "right_line": 1642 + }, + { + "left_line": 1637, + "right_line": 1643 + }, + { + "left_line": 1638, + "right_line": 1644 + }, + { + "left_line": 1639, + "right_line": 1645 + }, + { + "left_line": 1640, + "right_line": 1646 + }, + { + "left_line": 1641, + "right_line": 1647 + }, + { + "left_line": 1642, + "right_line": 1648 + }, + { + "left_line": 1643, + "right_line": 1649 + }, + { + "left_line": 1644, + "right_line": 1650 + }, + { + "left_line": 1645, + "right_line": 1651 + }, + { + "left_line": 1646, + "right_line": 1652 + }, + { + "left_line": 1647, + "right_line": 1653 + }, + { + "left_line": 1648, + "right_line": 1654 + }, + { + "left_line": 1649, + "right_line": 1655 + }, + { + "left_line": 1650, + "right_line": 1656 + }, + { + "left_line": 1651, + "right_line": 1657 + }, + { + "left_line": 1652, + "right_line": 1658 + }, + { + "left_line": 1653, + "right_line": 1659 + }, + { + "left_line": 1654, + "right_line": 1660 + }, + { + "left_line": 1655, + "right_line": 1661 + }, + { + "left_line": 1656, + "right_line": 1662 + }, + { + "left_line": 1657, + "right_line": 1663 + }, + { + "left_line": 1658, + "right_line": 1664 + }, + { + "left_line": 1659, + "right_line": 1665 + }, + { + "left_line": 1660, + "right_line": 1666 + }, + { + "left_line": 1661, + "right_line": 1667 + }, + { + "left_line": 1662, + "right_line": 1668 + }, + { + "left_line": 1663, + "right_line": 1669 + }, + { + "left_line": 1664, + "right_line": 1670 + }, + { + "left_line": 1665, + "right_line": 1671 + }, + { + "left_line": 1666, + "right_line": 1672 + }, + { + "left_line": 1667, + "right_line": 1673 + }, + { + "left_line": 1668, + "right_line": 1674 + }, + { + "left_line": 1669, + "right_line": 1675 + }, + { + "left_line": 1670, + "right_line": 1676 + }, + { + "left_line": 1671, + "right_line": 1677 + }, + { + "left_line": 1672, + "right_line": 1678 + }, + { + "left_line": 1673, + "right_line": 1679 + }, + { + "left_line": 1674, + "right_line": 1680 + }, + { + "left_line": 1675, + "right_line": 1681 + }, + { + "left_line": 1676, + "right_line": 1682 + }, + { + "left_line": 1677, + "right_line": 1683 + }, + { + "left_line": 1678, + "right_line": 1684 + }, + { + "left_line": 1679, + "right_line": 1685 + }, + { + "left_line": 1680, + "right_line": 1686 + }, + { + "left_line": 1681, + "right_line": 1687 + }, + { + "left_line": 1682, + "right_line": 1688 + }, + { + "left_line": 1683, + "right_line": 1689 + }, + { + "left_line": 1684, + "right_line": 1690 + }, + { + "left_line": 1685, + "right_line": 1691 + }, + { + "left_line": 1686, + "right_line": 1692 + }, + { + "left_line": 1687, + "right_line": 1693 + }, + { + "left_line": 1688, + "right_line": 1694 + }, + { + "left_line": 1689, + "right_line": 1695 + }, + { + "left_line": 1690, + "right_line": 1696 + }, + { + "left_line": 1691, + "right_line": 1697 + }, + { + "left_line": 1692, + "right_line": 1698 + }, + { + "left_line": 1693, + "right_line": 1699 + }, + { + "left_line": 1694, + "right_line": 1700 + }, + { + "left_line": 1695, + "right_line": 1701 + }, + { + "left_line": 1696, + "right_line": 1702 + }, + { + "left_line": 1697, + "right_line": 1703 + }, + { + "left_line": 1698, + "right_line": 1704 + }, + { + "left_line": 1699, + "right_line": 1705 + }, + { + "left_line": 1700, + "right_line": 1706 + }, + { + "left_line": 1701, + "right_line": 1707 + }, + { + "left_line": 1702, + "right_line": 1708 + }, + { + "left_line": 1703, + "right_line": 1709 + }, + { + "left_line": 1704, + "right_line": 1710 + }, + { + "left_line": 1705, + "right_line": 1711 + }, + { + "left_line": 1706, + "right_line": 1712 + }, + { + "left_line": 1707, + "right_line": 1713 + }, + { + "left_line": 1708, + "right_line": 1714 + }, + { + "left_line": 1709, + "right_line": 1715 + }, + { + "left_line": 1710, + "right_line": 1716 + }, + { + "left_line": 1711, + "right_line": 1717 + }, + { + "left_line": 1712, + "right_line": 1718 + }, + { + "left_line": 1713, + "right_line": 1719 + }, + { + "left_line": 1714, + "right_line": 1720 + }, + { + "left_line": 1715, + "right_line": 1721 + }, + { + "left_line": 1716, + "right_line": 1722 + }, + { + "left_line": 1717, + "right_line": 1723 + }, + { + "left_line": 1718, + "right_line": 1724 + }, + { + "left_line": 1719, + "right_line": 1725 + }, + { + "left_line": 1720, + "right_line": 1726 + }, + { + "left_line": 1721, + "right_line": 1727 + }, + { + "left_line": 1722, + "right_line": 1728 + }, + { + "left_line": 1723, + "right_line": 1729 + }, + { + "left_line": 1724, + "right_line": 1730 + }, + { + "left_line": 1725, + "right_line": 1731 + }, + { + "left_line": 1726, + "right_line": 1732 + }, + { + "left_line": 1727, + "right_line": 1733 + }, + { + "left_line": 1728, + "right_line": 1734 + }, + { + "left_line": 1729, + "right_line": 1735 + }, + { + "left_line": 1730, + "right_line": 1736 + }, + { + "left_line": 1731, + "right_line": 1737 + }, + { + "left_line": 1732, + "right_line": 1738 + }, + { + "left_line": 1733, + "right_line": 1739 + }, + { + "left_line": 1734, + "right_line": 1740 + }, + { + "left_line": 1735, + "right_line": 1741 + }, + { + "left_line": 1736, + "right_line": 1742 + }, + { + "left_line": 1737, + "right_line": 1743 + }, + { + "left_line": 1738, + "right_line": 1744 + }, + { + "left_line": 1739, + "right_line": 1745 + }, + { + "left_line": 1740, + "right_line": 1746 + }, + { + "left_line": 1741, + "right_line": 1747 + }, + { + "left_line": 1742, + "right_line": 1748 + }, + { + "left_line": 1743, + "right_line": 1749 + }, + { + "left_line": 1744, + "right_line": 1750 + }, + { + "left_line": 1745, + "right_line": 1751 + }, + { + "left_line": 1746, + "right_line": 1752 + }, + { + "left_line": 1747, + "right_line": 1753 + }, + { + "left_line": 1748, + "right_line": 1754 + }, + { + "left_line": 1749, + "right_line": 1755 + }, + { + "left_line": 1750, + "right_line": 1756 + }, + { + "left_line": 1751, + "right_line": 1757 + }, + { + "left_line": 1752, + "right_line": 1758 + }, + { + "left_line": 1753, + "right_line": 1759 + }, + { + "left_line": 1754, + "right_line": 1760 + }, + { + "left_line": 1755, + "right_line": 1761 + }, + { + "left_line": 1756, + "right_line": 1762 + }, + { + "left_line": 1757, + "right_line": 1763 + }, + { + "left_line": 1758, + "right_line": 1764 + }, + { + "left_line": 1759, + "right_line": 1765 + }, + { + "left_line": 1760, + "right_line": 1766 + }, + { + "left_line": 1761, + "right_line": 1767 + }, + { + "left_line": 1762, + "right_line": 1768 + }, + { + "left_line": 1763, + "right_line": 1769 + }, + { + "left_line": 1764, + "right_line": 1770 + }, + { + "left_line": 1765, + "right_line": 1771 + }, + { + "left_line": 1766, + "right_line": 1772 + }, + { + "left_line": 1767, + "right_line": 1773 + }, + { + "left_line": 1768, + "right_line": 1774 + }, + { + "left_line": 1769, + "right_line": 1775 + }, + { + "left_line": 1770, + "right_line": 1776 + }, + { + "left_line": 1771, + "right_line": 1777 + }, + { + "left_line": 1772, + "right_line": 1778 + }, + { + "left_line": 1773, + "right_line": 1779 + }, + { + "left_line": 1774, + "right_line": 1780 + }, + { + "left_line": 1775, + "right_line": 1781 + }, + { + "left_line": 1776, + "right_line": 1782 + }, + { + "left_line": 1777, + "right_line": 1783 + }, + { + "left_line": 1778, + "right_line": 1784 + }, + { + "left_line": 1779, + "right_line": 1785 + }, + { + "left_line": 1780, + "right_line": 1786 + }, + { + "left_line": 1781, + "right_line": 1787 + }, + { + "left_line": 1782, + "right_line": 1788 + }, + { + "left_line": 1783, + "right_line": 1789 + }, + { + "left_line": 1784, + "right_line": 1790 + }, + { + "left_line": 1785, + "right_line": 1791 + }, + { + "left_line": 1786, + "right_line": 1792 + }, + { + "left_line": 1787, + "right_line": 1793 + }, + { + "left_line": 1788, + "right_line": 1794 + }, + { + "left_line": 1789, + "right_line": 1795 + }, + { + "left_line": 1790, + "right_line": 1796 + }, + { + "left_line": 1791, + "right_line": 1797 + }, + { + "left_line": 1792, + "right_line": 1798 + }, + { + "left_line": 1793, + "right_line": 1799 + }, + { + "left_line": 1794, + "right_line": 1800 + }, + { + "left_line": 1795, + "right_line": 1801 + }, + { + "left_line": 1796, + "right_line": 1802 + }, + { + "left_line": 1797, + "right_line": 1803 + }, + { + "left_line": 1798, + "right_line": 1804 + }, + { + "left_line": 1799, + "right_line": 1805 + }, + { + "left_line": 1800, + "right_line": 1806 + }, + { + "left_line": 1801, + "right_line": 1807 + }, + { + "left_line": 1802, + "right_line": 1808 + }, + { + "left_line": 1803, + "right_line": 1809 + }, + { + "left_line": 1804, + "right_line": 1810 + }, + { + "left_line": 1805, + "right_line": 1811 + }, + { + "left_line": 1806, + "right_line": 1812 + }, + { + "left_line": 1807, + "right_line": 1813 + }, + { + "left_line": 1808, + "right_line": 1814 + }, + { + "left_line": 1809, + "right_line": 1815 + }, + { + "left_line": 1810, + "right_line": 1816 + }, + { + "left_line": 1811, + "right_line": 1817 + }, + { + "left_line": 1812, + "right_line": 1818 + }, + { + "left_line": 1813, + "right_line": 1819 + }, + { + "left_line": 1814, + "right_line": 1820 + }, + { + "left_line": 1815, + "right_line": 1821 + }, + { + "left_line": 1816, + "right_line": 1822 + }, + { + "left_line": 1817, + "right_line": 1823 + }, + { + "left_line": 1818, + "right_line": 1824 + }, + { + "left_line": 1819, + "right_line": 1825 + }, + { + "left_line": 1820, + "right_line": 1826 + }, + { + "left_line": 1821, + "right_line": 1827 + }, + { + "left_line": 1822, + "right_line": 1828 + }, + { + "left_line": 1823, + "right_line": 1829 + }, + { + "left_line": 1824, + "right_line": 1830 + }, + { + "left_line": 1825, + "right_line": 1831 + }, + { + "left_line": 1826, + "right_line": 1832 + }, + { + "left_line": 1827, + "right_line": 1833 + }, + { + "left_line": 1828, + "right_line": 1834 + }, + { + "left_line": 1829, + "right_line": 1835 + }, + { + "left_line": 1830, + "right_line": 1836 + }, + { + "left_line": 1831, + "right_line": 1837 + }, + { + "left_line": 1832, + "right_line": 1838 + }, + { + "left_line": 1833, + "right_line": 1839 + }, + { + "left_line": 1834, + "right_line": 1840 + }, + { + "left_line": 1835, + "right_line": 1841 + }, + { + "left_line": 1836, + "right_line": 1842 + }, + { + "left_line": 1837, + "right_line": 1843 + }, + { + "left_line": 1838, + "right_line": 1844 + }, + { + "left_line": 1839, + "right_line": 1845 + }, + { + "left_line": 1840, + "right_line": 1846 + }, + { + "left_line": 1841, + "right_line": 1847 + }, + { + "left_line": 1842, + "right_line": 1848 + }, + { + "left_line": 1843, + "right_line": 1849 + }, + { + "left_line": 1844, + "right_line": 1850 + }, + { + "left_line": 1845, + "right_line": 1851 + }, + { + "left_line": 1846, + "right_line": 1852 + }, + { + "left_line": 1847, + "right_line": 1853 + }, + { + "left_line": 1848, + "right_line": 1854 + }, + { + "left_line": 1849, + "right_line": 1855 + }, + { + "left_line": 1850, + "right_line": 1856 + }, + { + "left_line": 1851, + "right_line": 1857 + }, + { + "left_line": 1852, + "right_line": 1858 + }, + { + "left_line": 1853, + "right_line": 1859 + }, + { + "left_line": 1854, + "right_line": 1860 + }, + { + "left_line": 1855, + "right_line": 1861 + }, + { + "left_line": 1856, + "right_line": 1862 + }, + { + "left_line": 1857, + "right_line": 1863 + }, + { + "left_line": 1858, + "right_line": 1864 + }, + { + "left_line": 1859, + "right_line": 1865 + }, + { + "left_line": 1860, + "right_line": 1866 + }, + { + "left_line": 1861, + "right_line": 1867 + }, + { + "left_line": 1862, + "right_line": 1868 + }, + { + "left_line": 1863, + "right_line": 1869 + }, + { + "left_line": 1864, + "right_line": 1870 + }, + { + "left_line": 1865, + "right_line": 1871 + }, + { + "left_line": 1866, + "right_line": 1872 + }, + { + "left_line": 1867, + "right_line": 1873 + }, + { + "left_line": 1868, + "right_line": 1874 + }, + { + "left_line": 1869, + "right_line": 1875 + }, + { + "left_line": 1870, + "right_line": 1876 + }, + { + "left_line": 1871, + "right_line": 1877 + }, + { + "left_line": 1872, + "right_line": 1878 + }, + { + "left_line": 1873, + "right_line": 1879 + }, + { + "left_line": 1874, + "right_line": 1880 + }, + { + "left_line": 1875, + "right_line": 1881 + }, + { + "left_line": 1876, + "right_line": 1882 + }, + { + "left_line": 1877, + "right_line": 1883 + }, + { + "left_line": 1878, + "right_line": 1884 + }, + { + "left_line": 1879, + "right_line": 1885 + }, + { + "left_line": 1880, + "right_line": 1886 + }, + { + "left_line": 1881, + "right_line": 1887 + }, + { + "left_line": 1882, + "right_line": 1888 + }, + { + "left_line": 1883, + "right_line": 1889 + }, + { + "left_line": 1884, + "right_line": 1890 + }, + { + "left_line": 1885, + "right_line": 1891 + }, + { + "left_line": 1886, + "right_line": 1892 + }, + { + "left_line": 1887, + "right_line": 1893 + }, + { + "left_line": 1888, + "right_line": 1894 + }, + { + "left_line": 1889, + "right_line": 1895 + }, + { + "left_line": 1890, + "right_line": 1896 + }, + { + "left_line": 1891, + "right_line": 1897 + }, + { + "left_line": 1892, + "right_line": 1898 + }, + { + "left_line": 1893, + "right_line": 1899 + }, + { + "left_line": 1894, + "right_line": 1900 + }, + { + "left_line": 1895, + "right_line": 1901 + }, + { + "left_line": 1896, + "right_line": 1902 + }, + { + "left_line": 1897, + "right_line": 1903 + }, + { + "left_line": 1898, + "right_line": 1904 + }, + { + "left_line": 1899, + "right_line": 1905 + }, + { + "left_line": 1900, + "right_line": 1906 + }, + { + "left_line": 1901, + "right_line": 1907 + }, + { + "left_line": 1902, + "right_line": 1908 + }, + { + "left_line": 1903, + "right_line": 1909 + }, + { + "left_line": 1904, + "right_line": 1910 + }, + { + "left_line": 1905, + "right_line": 1911 + }, + { + "left_line": 1906, + "right_line": 1912 + }, + { + "left_line": 1907, + "right_line": 1913 + }, + { + "left_line": 1908, + "right_line": 1914 + }, + { + "left_line": 1909, + "right_line": 1915 + }, + { + "left_line": 1910, + "right_line": 1916 + }, + { + "left_line": 1911, + "right_line": 1917 + }, + { + "left_line": 1912, + "right_line": 1918 + }, + { + "left_line": 1913, + "right_line": 1919 + }, + { + "left_line": 1914, + "right_line": 1920 + }, + { + "left_line": 1915, + "right_line": 1921 + }, + { + "left_line": 1916, + "right_line": 1922 + }, + { + "left_line": 1917, + "right_line": 1923 + }, + { + "left_line": 1918, + "right_line": 1924 + }, + { + "left_line": 1919, + "right_line": 1925 + }, + { + "left_line": 1920, + "right_line": 1926 + }, + { + "left_line": 1921, + "right_line": 1927 + }, + { + "left_line": 1922, + "right_line": 1928 + }, + { + "left_line": 1923, + "right_line": 1929 + }, + { + "left_line": 1924, + "right_line": 1930 + }, + { + "left_line": 1925, + "right_line": 1931 + }, + { + "left_line": 1926, + "right_line": 1932 + }, + { + "left_line": 1927, + "right_line": 1933 + }, + { + "left_line": 1928, + "right_line": 1934 + }, + { + "left_line": 1929, + "right_line": 1935 + }, + { + "left_line": 1930, + "right_line": 1936 + }, + { + "left_line": 1931, + "right_line": 1937 + }, + { + "left_line": 1932, + "right_line": 1938 + }, + { + "left_line": 1933, + "right_line": 1939 + }, + { + "left_line": 1934, + "right_line": 1940 + }, + { + "left_line": 1935, + "right_line": 1941 + }, + { + "left_line": 1936, + "right_line": 1942 + }, + { + "left_line": 1937, + "right_line": 1943 + }, + { + "left_line": 1938, + "right_line": 1944 + }, + { + "left_line": 1939, + "right_line": 1945 + }, + { + "left_line": 1940, + "right_line": 1946 + }, + { + "left_line": 1941, + "right_line": 1947 + }, + { + "left_line": 1942, + "right_line": 1948 + }, + { + "left_line": 1943, + "right_line": 1949 + }, + { + "left_line": 1944, + "right_line": 1950 + }, + { + "left_line": 1945, + "right_line": 1951 + }, + { + "left_line": 1946, + "right_line": 1952 + }, + { + "left_line": 1947, + "right_line": 1953 + }, + { + "left_line": 1948, + "right_line": 1954 + }, + { + "left_line": 1949, + "right_line": 1955 + }, + { + "left_line": 1950, + "right_line": 1956 + }, + { + "left_line": 1951, + "right_line": 1957 + }, + { + "left_line": 1952, + "right_line": 1958 + }, + { + "left_line": 1953, + "right_line": 1959 + }, + { + "left_line": 1954, + "right_line": 1960 + }, + { + "left_line": 1955, + "right_line": 1961 + }, + { + "left_line": 1956, + "right_line": 1962 + }, + { + "left_line": 1957, + "right_line": 1963 + }, + { + "left_line": 1958, + "right_line": 1964 + }, + { + "left_line": 1959, + "right_line": 1965 + }, + { + "left_line": 1960, + "right_line": 1966 + }, + { + "left_line": 1961, + "right_line": 1967 + }, + { + "left_line": 1962, + "right_line": 1968 + }, + { + "left_line": 1963, + "right_line": 1969 + }, + { + "left_line": 1964, + "right_line": 1970 + }, + { + "left_line": 1965, + "right_line": 1971 + }, + { + "left_line": 1966, + "right_line": 1972 + }, + { + "left_line": 1967, + "right_line": 1973 + }, + { + "left_line": 1968, + "right_line": 1974 + }, + { + "left_line": 1969, + "right_line": 1975 + }, + { + "left_line": 1970, + "right_line": 1976 + }, + { + "left_line": 1971, + "right_line": 1977 + }, + { + "left_line": 1972, + "right_line": 1978 + }, + { + "left_line": 1973, + "right_line": 1979 + }, + { + "left_line": 1974, + "right_line": 1980 + }, + { + "left_line": 1975, + "right_line": 1981 + }, + { + "left_line": 1976, + "right_line": 1982 + }, + { + "left_line": 1977, + "right_line": 1983 + }, + { + "left_line": 1978, + "right_line": 1984 + }, + { + "left_line": 1979, + "right_line": 1985 + }, + { + "left_line": 1980, + "right_line": 1986 + }, + { + "left_line": 1981, + "right_line": 1987 + }, + { + "left_line": 1982, + "right_line": 1988 + }, + { + "left_line": 1983, + "right_line": 1989 + }, + { + "left_line": 1984, + "right_line": 1990 + }, + { + "left_line": 1985, + "right_line": 1991 + }, + { + "left_line": 1986, + "right_line": 1992 + }, + { + "left_line": 1987, + "right_line": 1993 + }, + { + "left_line": 1988, + "right_line": 1994 + }, + { + "left_line": 1989, + "right_line": 1995 + }, + { + "left_line": 1990, + "right_line": 1996 + }, + { + "left_line": 1991, + "right_line": 1997 + }, + { + "left_line": 1992, + "right_line": 1998 + }, + { + "left_line": 1993, + "right_line": 1999 + }, + { + "left_line": 1994, + "right_line": 2000 + }, + { + "left_line": 1995, + "right_line": 2001 + }, + { + "left_line": 1996, + "right_line": 2002 + }, + { + "left_line": 1997, + "right_line": 2003 + }, + { + "left_line": 1998, + "right_line": 2004 + }, + { + "left_line": 1999, + "right_line": 2005 + }, + { + "left_line": 2000, + "right_line": 2006 + }, + { + "left_line": 2001, + "right_line": 2007 + }, + { + "left_line": 2002, + "right_line": 2008 + }, + { + "left_line": 2003, + "right_line": 2009 + }, + { + "left_line": 2004, + "right_line": 2010 + }, + { + "left_line": 2005, + "right_line": 2011 + }, + { + "left_line": 2006, + "right_line": 2012 + }, + { + "left_line": 2007, + "right_line": 2013 + }, + { + "left_line": 2008, + "right_line": 2014 + }, + { + "left_line": 2009, + "right_line": 2015 + }, + { + "left_line": 2010, + "right_line": 2016 + }, + { + "left_line": 2011, + "right_line": 2017 + }, + { + "left_line": 2012, + "right_line": 2018 + }, + { + "left_line": 2013, + "right_line": 2019 + }, + { + "left_line": 2014, + "right_line": 2020 + }, + { + "left_line": 2015, + "right_line": 2021 + }, + { + "left_line": 2016, + "right_line": 2022 + }, + { + "left_line": 2017, + "right_line": 2023 + }, + { + "left_line": 2018, + "right_line": 2024 + }, + { + "left_line": 2019, + "right_line": 2025 + }, + { + "left_line": 2020, + "right_line": 2026 + }, + { + "left_line": 2021, + "right_line": 2027 + }, + { + "left_line": 2022, + "right_line": 2028 + }, + { + "left_line": 2023, + "right_line": 2029 + }, + { + "left_line": 2024, + "right_line": 2030 + }, + { + "left_line": 2025, + "right_line": 2031 + }, + { + "left_line": 2026, + "right_line": 2032 + }, + { + "left_line": 2027, + "right_line": 2033 + }, + { + "left_line": 2028, + "right_line": 2034 + }, + { + "left_line": 2029, + "right_line": 2035 + }, + { + "left_line": 2030, + "right_line": 2036 + }, + { + "left_line": 2031, + "right_line": 2037 + }, + { + "left_line": 2032, + "right_line": 2038 + }, + { + "left_line": 2033, + "right_line": 2039 + }, + { + "left_line": 2034, + "right_line": 2040 + }, + { + "left_line": 2035, + "right_line": 2041 + }, + { + "left_line": 2036, + "right_line": 2042 + }, + { + "left_line": 2037, + "right_line": 2043 + }, + { + "left_line": 2038, + "right_line": 2044 + }, + { + "left_line": 2039, + "right_line": 2045 + }, + { + "left_line": 2040, + "right_line": 2046 + }, + { + "left_line": 2041, + "right_line": 2047 + }, + { + "left_line": 2042, + "right_line": 2048 + }, + { + "left_line": 2043, + "right_line": 2049 + }, + { + "left_line": 2044, + "right_line": 2050 + }, + { + "left_line": 2045, + "right_line": 2051 + }, + { + "left_line": 2046, + "right_line": 2052 + }, + { + "left_line": 2047, + "right_line": 2053 + }, + { + "left_line": 2048, + "right_line": 2054 + }, + { + "left_line": 2049, + "right_line": 2055 + }, + { + "left_line": 2050, + "right_line": 2056 + }, + { + "left_line": 2051, + "right_line": 2057 + }, + { + "left_line": 2052, + "right_line": 2058 + }, + { + "left_line": 2053, + "right_line": 2059 + }, + { + "left_line": 2054, + "right_line": 2060 + }, + { + "left_line": 2055, + "right_line": 2061 + }, + { + "left_line": 2056, + "right_line": 2062 + }, + { + "left_line": 2057, + "right_line": 2063 + }, + { + "left_line": 2058, + "right_line": 2064 + }, + { + "left_line": 2059, + "right_line": 2065 + }, + { + "left_line": 2060, + "right_line": 2066 + }, + { + "left_line": 2061, + "right_line": 2067 + }, + { + "left_line": 2062, + "right_line": 2068 + }, + { + "left_line": 2063, + "right_line": 2069 + }, + { + "left_line": 2064, + "right_line": 2070 + }, + { + "left_line": 2065, + "right_line": 2071 + }, + { + "left_line": 2066, + "right_line": 2072 + }, + { + "left_line": 2067, + "right_line": 2073 + }, + { + "left_line": 2068, + "right_line": 2074 + }, + { + "left_line": 2069, + "right_line": 2075 + }, + { + "left_line": 2070, + "right_line": 2076 + }, + { + "left_line": 2071, + "right_line": 2077 + }, + { + "left_line": 2072, + "right_line": 2078 + }, + { + "left_line": 2073, + "right_line": 2079 + }, + { + "left_line": 2074, + "right_line": 2080 + }, + { + "left_line": 2075, + "right_line": 2081 + }, + { + "left_line": 2076, + "right_line": 2082 + }, + { + "left_line": 2077, + "right_line": 2083 + }, + { + "left_line": 2078, + "right_line": 2084 + }, + { + "left_line": 2079, + "right_line": 2085 + }, + { + "left_line": 2080, + "right_line": 2086 + }, + { + "left_line": 2081, + "right_line": 2087 + }, + { + "left_line": 2082, + "right_line": 2088 + }, + { + "left_line": 2083, + "right_line": 2089 + }, + { + "left_line": 2084, + "right_line": 2090 + }, + { + "left_line": 2085, + "right_line": 2091 + }, + { + "left_line": 2086, + "right_line": 2092 + }, + { + "left_line": 2087, + "right_line": 2093 + }, + { + "left_line": 2088, + "right_line": 2094 + }, + { + "left_line": 2089, + "right_line": 2095 + }, + { + "left_line": 2090, + "right_line": 2096 + }, + { + "left_line": 2091, + "right_line": 2097 + }, + { + "left_line": 2092, + "right_line": 2098 + }, + { + "left_line": 2093, + "right_line": 2099 + }, + { + "left_line": 2094, + "right_line": 2100 + }, + { + "left_line": 2095, + "right_line": 2101 + }, + { + "left_line": 2096, + "right_line": 2102 + }, + { + "left_line": 2097, + "right_line": 2103 + }, + { + "left_line": 2098, + "right_line": 2104 + }, + { + "left_line": 2099, + "right_line": 2105 + }, + { + "left_line": 2100, + "right_line": 2106 + }, + { + "left_line": 2101, + "right_line": 2107 + }, + { + "left_line": 2102, + "right_line": 2108 + }, + { + "left_line": 2103, + "right_line": 2109 + }, + { + "left_line": 2104, + "right_line": 2110 + }, + { + "left_line": 2105, + "right_line": 2111 + }, + { + "left_line": 2106, + "right_line": 2112 + }, + { + "left_line": 2107, + "right_line": 2113 + }, + { + "left_line": 2108, + "right_line": 2114 + }, + { + "left_line": 2109, + "right_line": 2115 + }, + { + "left_line": 2110, + "right_line": 2116 + }, + { + "left_line": 2111, + "right_line": 2117 + }, + { + "left_line": 2112, + "right_line": 2118 + }, + { + "left_line": 2113, + "right_line": 2119 + }, + { + "left_line": 2114, + "right_line": 2120 + }, + { + "left_line": 2115, + "right_line": 2121 + }, + { + "left_line": 2116, + "right_line": 2122 + }, + { + "left_line": 2117, + "right_line": 2123 + }, + { + "left_line": 2118, + "right_line": 2124 + }, + { + "left_line": 2119, + "right_line": 2125 + }, + { + "left_line": 2120, + "right_line": 2126 + }, + { + "left_line": 2121, + "right_line": 2127 + }, + { + "left_line": 2122, + "right_line": 2128 + }, + { + "left_line": 2123, + "right_line": 2129 + }, + { + "left_line": 2124, + "right_line": 2130 + }, + { + "left_line": 2125, + "right_line": 2131 + }, + { + "left_line": 2126, + "right_line": 2132 + }, + { + "left_line": 2127, + "right_line": 2133 + }, + { + "left_line": 2128, + "right_line": 2134 + }, + { + "left_line": 2129, + "right_line": 2135 + }, + { + "left_line": 2130, + "right_line": 2136 + }, + { + "left_line": 2131, + "right_line": 2137 + }, + { + "left_line": 2132, + "right_line": 2138 + }, + { + "left_line": 2133, + "right_line": 2139 + }, + { + "left_line": 2134, + "right_line": 2140 + }, + { + "left_line": 2135, + "right_line": 2141 + }, + { + "left_line": 2136, + "right_line": 2142 + }, + { + "left_line": 2137, + "right_line": 2143 + }, + { + "left_line": 2138, + "right_line": 2144 + }, + { + "left_line": 2139, + "right_line": 2145 + }, + { + "left_line": 2140, + "right_line": 2146 + }, + { + "left_line": 2141, + "right_line": 2147 + }, + { + "left_line": 2142, + "right_line": 2148 + }, + { + "left_line": 2143, + "right_line": 2149 + }, + { + "left_line": 2144, + "right_line": 2150 + }, + { + "left_line": 2145, + "right_line": 2151 + }, + { + "left_line": 2146, + "right_line": 2152 + }, + { + "left_line": 2147, + "right_line": 2153 + }, + { + "left_line": 2148, + "right_line": 2154 + }, + { + "left_line": 2149, + "right_line": 2155 + }, + { + "left_line": 2150, + "right_line": 2156 + }, + { + "left_line": 2151, + "right_line": 2157 + }, + { + "left_line": 2152, + "right_line": 2158 + }, + { + "left_line": 2153, + "right_line": 2159 + }, + { + "left_line": 2154, + "right_line": 2160 + }, + { + "left_line": 2155, + "right_line": 2161 + }, + { + "left_line": 2156, + "right_line": 2162 + }, + { + "left_line": 2157, + "right_line": 2163 + }, + { + "left_line": 2158, + "right_line": 2164 + }, + { + "left_line": 2159, + "right_line": 2165 + }, + { + "left_line": 2160, + "right_line": 2166 + }, + { + "left_line": 2161, + "right_line": 2167 + }, + { + "left_line": 2162, + "right_line": 2168 + }, + { + "left_line": 2163, + "right_line": 2169 + }, + { + "left_line": 2164, + "right_line": 2170 + }, + { + "left_line": 2165, + "right_line": 2171 + }, + { + "left_line": 2166, + "right_line": 2172 + }, + { + "left_line": 2167, + "right_line": 2173 + }, + { + "left_line": 2168, + "right_line": 2174 + }, + { + "left_line": 2169, + "right_line": 2175 + }, + { + "left_line": 2170, + "right_line": 2176 + }, + { + "left_line": 2171, + "right_line": 2177 + }, + { + "left_line": 2172, + "right_line": 2178 + }, + { + "left_line": 2173, + "right_line": 2179 + }, + { + "left_line": 2174, + "right_line": 2180 + }, + { + "left_line": 2175, + "right_line": 2181 + }, + { + "left_line": 2176, + "right_line": 2182 + }, + { + "left_line": 2177, + "right_line": 2183 + }, + { + "left_line": 2178, + "right_line": 2184 + }, + { + "left_line": 2179, + "right_line": 2185 + }, + { + "left_line": 2180, + "right_line": 2186 + }, + { + "left_line": 2181, + "right_line": 2187 + }, + { + "left_line": 2182, + "right_line": 2188 + }, + { + "left_line": 2183, + "right_line": 2189 + }, + { + "left_line": 2184, + "right_line": 2190 + }, + { + "left_line": 2185, + "right_line": 2191 + }, + { + "left_line": 2186, + "right_line": 2192 + }, + { + "left_line": 2187, + "right_line": 2193 + }, + { + "left_line": 2188, + "right_line": 2194 + }, + { + "left_line": 2189, + "right_line": 2195 + }, + { + "left_line": 2190, + "right_line": 2196 + }, + { + "left_line": 2191, + "right_line": 2197 + }, + { + "left_line": 2192, + "right_line": 2198 + }, + { + "left_line": 2193, + "right_line": 2199 + }, + { + "left_line": 2194, + "right_line": 2200 + }, + { + "left_line": 2195, + "right_line": 2201 + }, + { + "left_line": 2196, + "right_line": 2202 + }, + { + "left_line": 2197, + "right_line": 2203 + }, + { + "left_line": 2198, + "right_line": 2204 + }, + { + "left_line": 2199, + "right_line": 2205 + }, + { + "left_line": 2200, + "right_line": 2206 + }, + { + "left_line": 2201, + "right_line": 2207 + }, + { + "left_line": 2202, + "right_line": 2208 + }, + { + "left_line": 2203, + "right_line": 2209 + }, + { + "left_line": 2204, + "right_line": 2210 + }, + { + "left_line": 2205, + "right_line": 2211 + }, + { + "left_line": 2206, + "right_line": 2212 + }, + { + "left_line": 2207, + "right_line": 2213 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/metadata.json b/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/metadata.json new file mode 100644 index 0000000..408b340 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "e7ccc2be9954c35f32220f7c87216ea158382376", + "commit": "e81490eea468f3dce6665490508b6ff7c3625a9a", + "message": "Fix content-type leak in Mustermann::Set pass-block routing\n\nThe set-based route! path iterates only matching routes (set.match_all),", + "author": "Sean Floyd", + "date": "2026-06-25T12:39:33-04:00", + "language": "ruby", + "files_changed": 1, + "insertions": 7, + "deletions": 1 +} diff --git a/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/new.rb b/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/new.rb new file mode 100644 index 0000000..bbaf160 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/new.rb @@ -0,0 +1,2213 @@ +# frozen_string_literal: true + +# external dependencies +require 'rack' +begin + require 'rackup' +rescue LoadError +end +require 'tilt' +require 'rack/protection' +require 'rack/session' +require 'mustermann' +require 'mustermann/set' +require 'mustermann/sinatra' +require 'mustermann/regular' + +# stdlib dependencies +require 'ipaddr' +require 'time' +require 'uri' + +# other files we need +require 'sinatra/indifferent_hash' +require 'sinatra/show_exceptions' +require 'sinatra/version' + +require_relative 'middleware/logger' + +module Sinatra + # The request object. See Rack::Request for more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Request + class Request < Rack::Request + HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze + HEADER_VALUE_WITH_PARAMS = %r{(?:(?:\w+|\*)/(?:\w+(?:\.|-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*}.freeze + + # Returns an array of acceptable media types for the response + def accept + @env['sinatra.accept'] ||= if @env.include?('HTTP_ACCEPT') && (@env['HTTP_ACCEPT'].to_s != '') + @env['HTTP_ACCEPT'] + .to_s + .scan(HEADER_VALUE_WITH_PARAMS) + .map! { |e| AcceptEntry.new(e) } + .sort + else + [AcceptEntry.new('*/*')] + end + end + + def accept?(type) + preferred_type(type).to_s.include?(type) + end + + def preferred_type(*types) + return accept.first if types.empty? + + types.flatten! + return types.first if accept.empty? + + accept.detect do |accept_header| + type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) } + return type if type + end + end + + alias secure? ssl? + + def forwarded? + !forwarded_authority.nil? + end + + def safe? + get? || head? || options? || trace? + end + + def idempotent? + safe? || put? || delete? || link? || unlink? + end + + def link? + request_method == 'LINK' + end + + def unlink? + request_method == 'UNLINK' + end + + def params + super + rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e + raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}" + rescue EOFError => e + raise BadRequest, "Invalid multipart/form-data: #{Rack::Utils.escape_html(e.message)}" + end + + class AcceptEntry + attr_accessor :params + attr_reader :entry + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @entry = entry + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + @q = @params.delete('q') { 1.0 }.to_f + end + + def <=>(other) + other.priority <=> priority + end + + def priority + # We sort in descending order; better matches should be higher. + [@q, -@type.count('*'), @params.size] + end + + def to_str + @type + end + + def to_s(full = false) + full ? entry : to_str + end + + def respond_to?(*args) + super || to_str.respond_to?(*args) + end + + def method_missing(*args, &block) + to_str.send(*args, &block) + end + end + + class MimeTypeEntry + attr_reader :params + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + end + + def accepts?(entry) + File.fnmatch(entry, self) && matches_params?(entry.params) + end + + def to_str + @type + end + + def matches_params?(params) + return true if @params.empty? + + params.all? { |k, v| !@params.key?(k) || @params[k] == v } + end + end + end + + # The response object. See Rack::Response and Rack::Response::Helpers for + # more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Response + # https://rubydoc.info/github/rack/rack/main/Rack/Response/Helpers + class Response < Rack::Response + DROP_BODY_RESPONSES = [204, 304].freeze + + def body=(value) + value = value.body while Rack::Response === value + @body = String === value ? [value.to_str] : value + end + + def each + block_given? ? super : enum_for(:each) + end + + def finish + result = body + + if drop_content_info? + headers.delete 'content-length' + headers.delete 'content-type' + end + + if drop_body? + close + result = [] + end + + if calculate_content_length? + # if some other code has already set content-length, don't muck with it + # currently, this would be the static file-handler + headers['content-length'] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] + end + + private + + def calculate_content_length? + headers['content-type'] && !headers['content-length'] && (Array === body) + end + + def drop_content_info? + informational? || drop_body? + end + + def drop_body? + DROP_BODY_RESPONSES.include?(status) + end + end + + # Some Rack handlers implement an extended body object protocol, however, + # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question. + # This middleware will detect an extended body object and will make sure it reaches the + # handler directly. We do this here, so our middleware and middleware set up by the app will + # still be able to run. + class ExtendedRack < Struct.new(:app) + def call(env) + result = app.call(env) + callback = env['async.callback'] + return result unless callback && async?(*result) + + after_response { callback.call result } + setup_close(env, *result) + throw :async + end + + private + + def setup_close(env, _status, _headers, body) + return unless body.respond_to?(:close) && env.include?('async.close') + + env['async.close'].callback { body.close } + env['async.close'].errback { body.close } + end + + def after_response(&block) + raise NotImplementedError, 'only supports EventMachine at the moment' unless defined? EventMachine + + EventMachine.next_tick(&block) + end + + def async?(status, _headers, body) + return true if status == -1 + + body.respond_to?(:callback) && body.respond_to?(:errback) + end + end + + # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing, + # if another CommonLogger is already in the middleware chain. + class CommonLogger < Rack::CommonLogger + def call(env) + env['sinatra.commonlogger'] ? @app.call(env) : super + end + + superclass.class_eval do + alias_method :call_without_check, :call unless method_defined? :call_without_check + def call(env) + env['sinatra.commonlogger'] = true + call_without_check(env) + end + end + end + + class Error < StandardError # :nodoc: + end + + class BadRequest < Error # :nodoc: + def http_status; 400 end + end + + class NotFound < Error # :nodoc: + def http_status; 404 end + end + + # Methods available to routes, before/after filters, and views. + module Helpers + # Set or retrieve the response status code. + def status(value = nil) + response.status = Rack::Utils.status_code(value) if value + response.status + end + + # Set or retrieve the response body. When a block is given, + # evaluation is deferred until the body is read with #each. + def body(value = nil, &block) + if block_given? + def block.each; yield(call) end + response.body = block + elsif value + unless request.head? || value.is_a?(Rack::Files::BaseIterator) || value.is_a?(Stream) + headers.delete 'content-length' + end + response.body = value + else + response.body + end + end + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) + http_version = env['SERVER_PROTOCOL'] + if (http_version == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') + status 303 + else + status 302 + end + + # According to RFC 2616 section 14.30, "the field value consists of a + # single absolute URI" + response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?) + halt(*args) + end + + # Generates the absolute URI for a given path in the app. + # Takes Rack routers and reverse proxies into account. + def uri(addr = nil, absolute = true, add_script_name = true) + return addr if addr.to_s =~ /\A[a-z][a-z0-9+.\-]*:/i + + uri = [host = String.new] + if absolute + host << "http#{'s' if request.secure?}://" + host << if request.forwarded? || (request.port != (request.secure? ? 443 : 80)) + request.host_with_port + else + request.host + end + end + uri << request.script_name.to_s if add_script_name + uri << (addr || request.path_info).to_s + File.join uri + end + + alias url uri + alias to uri + + # Halt processing and return the error status provided. + def error(code, body = nil) + if code.respond_to? :to_str + body = code.to_str + code = 500 + end + response.body = body unless body.nil? + halt code + end + + # Halt processing and return a 404 Not Found. + def not_found(body = nil) + error 404, body + end + + # Set multiple response headers with Hash. + def headers(hash = nil) + response.headers.merge! hash if hash + response.headers + end + + # Access the underlying Rack session. + def session + request.session + end + + # Access shared logger object. + def logger + request.logger + end + + # Look up a media type by file extension in Rack's mime registry. + def mime_type(type) + Base.mime_type(type) + end + + # Set the content-type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) + return response['content-type'] unless type + + default = params.delete :default + mime_type = mime_type(type) || default + raise format('Unknown media type: %p', type) if mime_type.nil? + + mime_type = mime_type.dup + unless params.include?(:charset) || settings.add_charset.all? { |p| !(p === mime_type) } + params[:charset] = params.delete('charset') || settings.default_encoding + end + params.delete :charset if mime_type.include? 'charset' + unless params.empty? + mime_type << ';' + mime_type << params.map do |key, val| + val = val.inspect if val.to_s =~ /[";,]/ + "#{key}=#{val}" + end.join(';') + end + response['content-type'] = mime_type + end + + # https://html.spec.whatwg.org/#multipart-form-data + MULTIPART_FORM_DATA_REPLACEMENT_TABLE = { + '"' => '%22', + "\r" => '%0D', + "\n" => '%0A' + }.freeze + + # Set the Content-Disposition to "attachment" with the specified filename, + # instructing the user agents to prompt to save. + def attachment(filename = nil, disposition = :attachment) + response['Content-Disposition'] = disposition.to_s.dup + return unless filename + + params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)) + response['Content-Disposition'] << params + ext = File.extname(filename) + content_type(ext) unless response['content-type'] || ext.empty? + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) + if opts[:type] || !response['content-type'] + content_type opts[:type] || File.extname(path), default: 'application/octet-stream' + end + + disposition = opts[:disposition] + filename = opts[:filename] + disposition = :attachment if disposition.nil? && filename + filename = path if filename.nil? + attachment(filename, disposition) if disposition + + last_modified opts[:last_modified] if opts[:last_modified] + + file = Rack::Files.new(File.dirname(settings.app_file)) + result = file.serving(request, path) + + result[1].each { |k, v| headers[k] ||= v } + headers['content-length'] = result[1]['content-length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT + not_found + end + + # Class of the response body in case you use #stream. + # + # Three things really matter: The front and back block (back being the + # block generating content, front the one sending it to the client) and + # the scheduler, integrating with whatever concurrency feature the Rack + # handler is using. + # + # Scheduler has to respond to defer and schedule. + class Stream + def self.schedule(*) yield end + def self.defer(*) yield end + + def initialize(scheduler = self.class, keep_open = false, &back) + @back = back.to_proc + @scheduler = scheduler + @keep_open = keep_open + @callbacks = [] + @closed = false + end + + def close + return if closed? + + @closed = true + @scheduler.schedule { @callbacks.each { |c| c.call } } + end + + def each(&front) + @front = front + @scheduler.defer do + begin + @back.call(self) + rescue Exception => e + @scheduler.schedule { raise e } + ensure + close unless @keep_open + end + end + end + + def <<(data) + @scheduler.schedule { @front.call(data.to_s) } + self + end + + def callback(&block) + return yield if closed? + + @callbacks << block + end + + alias errback callback + + def closed? + @closed + end + end + + # Allows to start sending data to the client even though later parts of + # the response body have not yet been generated. + # + # The close parameter specifies whether Stream#close should be called + # after the block has been executed. + def stream(keep_open = false) + scheduler = env['async.callback'] ? EventMachine : Stream + current = @params.dup + stream = if scheduler == Stream && keep_open + Stream.new(scheduler, false) do |out| + until out.closed? + with_params(current) { yield(out) } + end + end + else + Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } + end + body stream + end + + # Specify response freshness policy for HTTP caches (Cache-Control header). + # Any number of non-value directives (:public, :private, :no_cache, + # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with + # a Hash of value directives (:max_age, :s_maxage). + # + # cache_control :public, :must_revalidate, :max_age => 60 + # => Cache-Control: public, must-revalidate, max-age=60 + # + # See RFC 2616 / 14.9 for more on standard cache control directives: + # http://tools.ietf.org/html/rfc2616#section-14.9.1 + def cache_control(*values) + if values.last.is_a?(Hash) + hash = values.pop + hash.reject! { |_k, v| v == false } + hash.reject! { |k, v| values << k if v == true } + else + hash = {} + end + + values.map! { |value| value.to_s.tr('_', '-') } + hash.each do |key, value| + key = key.to_s.tr('_', '-') + value = value.to_i if %w[max-age s-maxage].include? key + values << "#{key}=#{value}" + end + + response['Cache-Control'] = values.join(', ') if values.any? + end + + # Set the Expires header and Cache-Control/max-age directive. Amount + # can be an integer number of seconds in the future or a Time object + # indicating when the response should be considered "stale". The remaining + # "values" arguments are passed to the #cache_control helper: + # + # expires 500, :public, :must_revalidate + # => Cache-Control: public, must-revalidate, max-age=500 + # => Expires: Mon, 08 Jun 2009 08:50:17 GMT + # + def expires(amount, *values) + values << {} unless values.last.is_a?(Hash) + + if amount.is_a? Integer + time = Time.now + amount.to_i + max_age = amount + else + time = time_for amount + max_age = time - Time.now + end + + values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 } + cache_control(*values) + + response['Expires'] = time.httpdate + end + + # Set the last modified time of the resource (HTTP 'Last-Modified' header) + # and halt if conditional GET matches. The +time+ argument is a Time, + # DateTime, or other object that responds to +to_time+. + # + # When the current request includes an 'If-Modified-Since' header that is + # equal or later than the time specified, execution is immediately halted + # with a '304 Not Modified' response. + def last_modified(time) + return unless time + + time = time_for time + response['Last-Modified'] = time.httpdate + return if env['HTTP_IF_NONE_MATCH'] + + if (status == 200) && env['HTTP_IF_MODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i + halt 304 if since >= time.to_i + end + + if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i + halt 412 if since < time.to_i + end + rescue ArgumentError + end + + ETAG_KINDS = %i[strong weak].freeze + # Set the response entity tag (HTTP 'ETag' header) and halt if conditional + # GET matches. The +value+ argument is an identifier that uniquely + # identifies the current version of the resource. The +kind+ argument + # indicates whether the etag should be used as a :strong (default) or :weak + # cache validator. + # + # When the current request includes an 'If-None-Match' header with a + # matching etag, execution is immediately halted. If the request method is + # GET or HEAD, a '304 Not Modified' response is sent. + def etag(value, options = {}) + # Before touching this code, please double check RFC 2616 14.24 and 14.26. + options = { kind: options } unless Hash === options + kind = options[:kind] || :strong + new_resource = options.fetch(:new_resource) { request.post? } + + unless ETAG_KINDS.include?(kind) + raise ArgumentError, ':strong or :weak expected' + end + + value = format('"%s"', value) + value = "W/#{value}" if kind == :weak + response['ETag'] = value + + return unless success? || status == 304 + + if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource) + halt(request.safe? ? 304 : 412) + end + + if env['HTTP_IF_MATCH'] + return if etag_matches?(env['HTTP_IF_MATCH'], new_resource) + + halt 412 + end + + nil + end + + # Sugar for redirect (example: redirect back) + def back + request.referer + end + + # whether or not the status is set to 1xx + def informational? + status.between? 100, 199 + end + + # whether or not the status is set to 2xx + def success? + status.between? 200, 299 + end + + # whether or not the status is set to 3xx + def redirect? + status.between? 300, 399 + end + + # whether or not the status is set to 4xx + def client_error? + status.between? 400, 499 + end + + # whether or not the status is set to 5xx + def server_error? + status.between? 500, 599 + end + + # whether or not the status is set to 404 + def not_found? + status == 404 + end + + # whether or not the status is set to 400 + def bad_request? + status == 400 + end + + # Generates a Time object from the given value. + # Used by #expires and #last_modified. + def time_for(value) + if value.is_a? Numeric + Time.at value + elsif value.respond_to? :to_s + Time.parse value.to_s + else + value.to_time + end + rescue ArgumentError => e + raise e + rescue Exception + raise ArgumentError, "unable to convert #{value.inspect} to a Time object" + end + + private + + # Helper method checking if a ETag value list includes the current ETag. + def etag_matches?(list, new_resource = request.post?) + return !new_resource if list == '*' + + list.to_s.split(',').map(&:strip).include?(response['ETag']) + end + + def with_params(temp_params) + original = @params + @params = temp_params + yield + ensure + @params = original if original + end + end + + # Template rendering methods. Each method takes the name of a template + # to render as a Symbol and returns a String with the rendered output, + # as well as an optional hash with additional options. + # + # `template` is either the name or path of the template as symbol + # (Use `:'subdir/myview'` for views in subdirectories), or a string + # that will be rendered. + # + # Possible options are: + # :content_type The content type to use, same arguments as content_type. + # :layout If set to something falsy, no layout is rendered, otherwise + # the specified layout is used (Ignored for `sass`) + # :layout_engine Engine to use for rendering the layout. + # :locals A hash with local variables that should be available + # in the template + # :scope If set, template is evaluate with the binding of the given + # object rather than the application instance. + # :views Views directory to use. + module Templates + module ContentTyped + attr_accessor :content_type + end + + def initialize + super + @default_layout = :layout + @preferred_extension = nil + end + + def erb(template, options = {}, locals = {}, &block) + render(:erb, template, options, locals, &block) + end + + def haml(template, options = {}, locals = {}, &block) + render(:haml, template, options, locals, &block) + end + + def sass(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :sass, template, options, locals + end + + def scss(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :scss, template, options, locals + end + + def builder(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:builder, template, options, locals, &block) + end + + def liquid(template, options = {}, locals = {}, &block) + render(:liquid, template, options, locals, &block) + end + + def markdown(template, options = {}, locals = {}) + options[:exclude_outvar] = true + render :markdown, template, options, locals + end + + def rdoc(template, options = {}, locals = {}) + render :rdoc, template, options, locals + end + + def asciidoc(template, options = {}, locals = {}) + render :asciidoc, template, options, locals + end + + def markaby(template = nil, options = {}, locals = {}, &block) + render_ruby(:mab, template, options, locals, &block) + end + + def nokogiri(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:nokogiri, template, options, locals, &block) + end + + def slim(template, options = {}, locals = {}, &block) + render(:slim, template, options, locals, &block) + end + + def yajl(template, options = {}, locals = {}) + options[:default_content_type] = :json + render :yajl, template, options, locals + end + + def rabl(template, options = {}, locals = {}) + Rabl.register! + render :rabl, template, options, locals + end + + # Calls the given block for every possible template file in views, + # named name.ext, where ext is registered on engine. + def find_template(views, name, engine) + yield ::File.join(views, "#{name}.#{@preferred_extension}") + + Tilt.default_mapping.extensions_for(engine).each do |ext| + yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension + end + end + + private + + # logic shared between builder and nokogiri + def render_ruby(engine, template, options = {}, locals = {}, &block) + if template.is_a?(Hash) + options = template + template = nil + end + template = proc { block } if template.nil? + render engine, template, options, locals + end + + def render(engine, data, options = {}, locals = {}, &block) + # merge app-level options + engine_options = settings.respond_to?(engine) ? settings.send(engine) : {} + options.merge!(engine_options) { |_key, v1, _v2| v1 } + + # extract generic options + locals = options.delete(:locals) || locals || {} + views = options.delete(:views) || settings.views || './views' + layout = options[:layout] + layout = false if layout.nil? && options.include?(:layout) + eat_errors = layout.nil? + layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false) + layout = @default_layout if layout.nil? || (layout == true) + layout_options = options.delete(:layout_options) || {} + content_type = options.delete(:default_content_type) + content_type = options.delete(:content_type) || content_type + layout_engine = options.delete(:layout_engine) || engine + scope = options.delete(:scope) || self + exclude_outvar = options.delete(:exclude_outvar) + options.delete(:layout) + + # set some defaults + options[:outvar] ||= '@_out_buf' unless exclude_outvar + options[:default_encoding] ||= settings.default_encoding + + # compile and render template + begin + layout_was = @default_layout + @default_layout = false + template = compile_template(engine, data, options, views) + output = template.render(scope, locals, &block) + ensure + @default_layout = layout_was + end + + # render layout + if layout + extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope } + options = options.merge(extra_options).merge!(layout_options) + + catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } } + end + + if content_type + # sass-embedded returns a frozen string + output = +output + output.extend(ContentTyped).content_type = content_type + end + output + end + + def compile_template(engine, data, options, views) + eat_errors = options.delete :eat_errors + template = Tilt[engine] + raise "Template engine not found: #{engine}" if template.nil? + + case data + when Symbol + template_cache.fetch engine, data, options, views do + body, path, line = settings.templates[data] + if body + body = body.call if body.respond_to?(:call) + template.new(path, line.to_i, options) { body } + else + found = false + @preferred_extension = engine.to_s + find_template(views, data, template) do |file| + path ||= file # keep the initial path rather than the last one + found = File.exist?(file) + if found + path = file + break + end + end + throw :layout_missing if eat_errors && !found + template.new(path, 1, options) + end + end + when Proc + compile_block_template(template, options, &data) + when String + template_cache.fetch engine, data, options, views do + compile_block_template(template, options) { data } + end + else + raise ArgumentError, "Sorry, don't know how to render #{data.inspect}." + end + end + + def compile_block_template(template, options, &body) + first_location = caller_locations.first + path = first_location.path + line = first_location.lineno + path = options[:path] || path + line = options[:line] || line + template.new(path, line.to_i, options, &body) + end + end + + # Extremely simple template cache implementation. + # * Not thread-safe. + # * Size is unbounded. + # * Keys are not copied defensively, and should not be modified after + # being passed to #fetch. More specifically, the values returned by + # key#hash and key#eql? should not change. + # + # Implementation copied from Tilt::Cache. + class TemplateCache + def initialize + @cache = {} + end + + # Caches a value for key, or returns the previously cached value. + # If a value has been previously cached for key then it is + # returned. Otherwise, block is yielded to and its return value + # which may be nil, is cached under key and returned. + def fetch(*key) + @cache.fetch(key) do + @cache[key] = yield + end + end + + # Clears the cache. + def clear + @cache = {} + end + end + + # Base class for all Sinatra applications and middleware. + class Base + include Rack::Utils + include Helpers + include Templates + + URI_INSTANCE = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new + + attr_accessor :app, :env, :request, :response, :params + attr_reader :template_cache + + def initialize(app = nil, **_kwargs) + super() + @app = app + @template_cache = TemplateCache.new + @pinned_response = nil # whether a before! filter pinned the content-type + yield self if block_given? + end + + # Rack call interface. + def call(env) + dup.call!(env) + end + + def call!(env) # :nodoc: + @env = env + @params = IndifferentHash.new + @request = Request.new(env) + @response = Response.new + @pinned_response = nil + template_cache.clear if settings.reload_templates + + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + + unless @response['content-type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif (default = settings.default_content_type) + content_type default + end + end + + @response.finish + end + + # Access settings defined with Base.set. + def self.settings + self + end + + # Access settings defined with Base.set. + def settings + self.class.settings + end + + # Exit the current block, halts any further processing + # of the request, and returns the specified response. + def halt(*response) + response = response.first if response.length == 1 + throw :halt, response + end + + # Pass control to the next matching route. + # If there are no more matching routes, Sinatra will + # return a 404 response. + def pass(&block) + throw :pass, block + end + + # Forward the request to the downstream app -- middleware only. + def forward + raise 'downstream app not set' unless @app.respond_to? :call + + status, headers, body = @app.call env + @response.status = status + @response.body = body + @response.headers.merge! headers + nil + end + + private + + # Run filters defined on the class and all superclasses. + # Accepts an optional block to call after each filter is applied. + def filter!(type, base = settings, &block) + filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters) + base.filters[type].each do |args| + result = process_route(*args) + block.call(result) if block_given? + end + end + + # Run routes defined on the class and all superclasses. + def route!(base = settings, pass_block = nil) + if set = base.route_sets[@request.request_method] + routes = set.match_all(route) + else + routes = base.routes[@request.request_method] + end + + routes&.each do |match_or_signature| + response.delete_header('content-type') unless @pinned_response + + if match_or_signature.is_a?(Mustermann::Match) + match = match_or_signature + pattern, conditions, block = match_or_signature.value + else + pattern, conditions, block = match_or_signature + end + + returned_pass_block = process_route(pattern, conditions, match: match) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" + route_eval { block[*args] } + end + + # don't wipe out pass_block in superclass + pass_block = returned_pass_block if returned_pass_block + end + + # Run routes defined in superclass. + if base.superclass.respond_to?(:routes) + return route!(base.superclass, pass_block) + end + + # Set-based routing only iterates matching routes, so the per-iteration + # reset above is skipped after a route that set a content-type then passed. + # Clear it so the pass block starts clean, as the legacy all-routes scan did. + if pass_block + response.delete_header('content-type') unless @pinned_response + route_eval(&pass_block) + end + route_missing + end + + # Run a route block and throw :halt with the result. + def route_eval + throw :halt, yield + end + + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, conditions, block = nil, values = [], match: nil) + if match + params = match.params + captures = match.captures + else + params = pattern.params(route) + return unless params + end + + params = params.dup + params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes + force_encoding(params) + + @params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any? + + regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) }) + if regexp_exists + match ||= pattern.match(route) + captures = match.captures.map { |c| URI_INSTANCE.unescape(c) if c } + values += captures + @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty? + else + values += params.values.flatten + end + + catch(:pass) do + conditions.each { |c| throw :pass if c.bind(self).call == false } + block ? block[self, values] : yield(self, values) + end + rescue StandardError + @env['sinatra.error.params'] = @params + raise + ensure + params ||= {} + params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params'] + end + + # The current route being processed. Used for pattern matching and URL generation. + def route + route = @request.path_info + route = '/' if route.empty? && !settings.empty_path_info? + route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/') + route + end + + # No matching route was found or all routes passed. The default + # implementation is to forward the request downstream when running + # as middleware (@app is non-nil); when no downstream app is set, raise + # a NotFound exception. Subclasses can override this method to perform + # custom route miss logic. + def route_missing + raise NotFound unless @app + + forward + end + + # Attempt to serve static files from public directory. Throws :halt when + # a matching file is found, returns nil otherwise. + # If custom static headers are defined, use them. + def static!(options = {}) + return if (public_dir = settings.public_folder).nil? + + path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" + return unless valid_path?(path) + + path = File.expand_path(path) + return unless path.start_with?("#{File.expand_path(public_dir)}/") + + return unless File.file?(path) + + env['sinatra.static_file'] = path + cache_control(*settings.static_cache_control) if settings.static_cache_control? + + headers(settings.static_headers) if settings.static_headers? + + send_file path, options.merge(disposition: nil) + end + + # Run the block with 'throw :halt' support and apply result to the response. + def invoke(&block) + res = catch(:halt, &block) + + res = [res] if (Integer === res) || (String === res) + if (Array === res) && (Integer === res.first) + res = res.dup + status(res.shift) + body(res.pop) + headers(*res) + elsif res.respond_to? :each + body res + end + nil # avoid double setting the same response tuple twice + end + + # Dispatch a request with error handling. + def dispatch! + # Avoid passing frozen string in force_encoding + @params.merge!(@request.params).each do |key, val| + next unless val.respond_to?(:force_encoding) + + val = val.dup if val.frozen? + @params[key] = force_encoding(val) + end + + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do + @pinned_response = !response['content-type'].nil? + end + route! + end + rescue ::Exception => e + invoke { handle_exception!(e) } + ensure + begin + filter! :after unless env['sinatra.static_file'] + rescue ::Exception => e + invoke { handle_exception!(e) } unless @env['sinatra.error'] + end + end + + # Error handling during requests. + def handle_exception!(boom) + error_params = @env['sinatra.error.params'] + + @params = @params.merge(error_params) if error_params + + @env['sinatra.error'] = boom + + http_status = if boom.is_a? Sinatra::Error + if boom.respond_to? :http_status + boom.http_status + elsif settings.use_code? && boom.respond_to?(:code) + boom.code + end + end + + http_status = 500 unless http_status&.between?(400, 599) + status(http_status) + + if server_error? + dump_errors! boom if settings.dump_errors? + raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler) + elsif not_found? + headers['X-Cascade'] = 'pass' if settings.x_cascade? + end + + if (res = error_block!(boom.class, boom) || error_block!(status, boom)) + return res + end + + if not_found? || bad_request? + if boom.message && boom.message != boom.class.name + body Rack::Utils.escape_html(boom.message) + else + content_type 'text/html' + body "

#{not_found? ? 'Not Found' : 'Bad Request'}

" + end + end + + return unless server_error? + + raise boom if settings.raise_errors? || settings.show_exceptions? + + error_block! Exception, boom + end + + # Find an custom error block for the key(s) specified. + def error_block!(key, *block_params) + base = settings + while base.respond_to?(:errors) + args_array = base.errors[key] + + next base = base.superclass unless args_array + + args_array.reverse_each do |args| + first = args == args_array.first + args += [block_params] + resp = process_route(*args) + return resp unless resp.nil? && !first + end + end + return false unless key.respond_to?(:superclass) && (key.superclass < Exception) + + error_block!(key.superclass, *block_params) + end + + def dump_errors!(boom) + if boom.respond_to?(:detailed_message) + msg = boom.detailed_message(highlight: false) + if msg =~ /\A(.*?)(?: \(#{ Regexp.quote(boom.class.to_s) }\))?\n/ + msg = $1 + additional_msg = $'.lines(chomp: true) + else + additional_msg = [] + end + else + msg = boom.message + additional_msg = [] + end + msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{msg}:", *additional_msg, *boom.backtrace].join("\n\t") + @env['rack.errors'].puts(msg) + end + + class << self + CALLERS_TO_IGNORE = [ # :nodoc: + %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code + %r{lib/tilt.*\.rb$}, # all tilt code + /^\(.*\)$/, # generated code + /\/bundled_gems.rb$/, # ruby >= 3.3 with bundler >= 2.5 + %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks + /active_support/, # active_support require hacks + %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks + /= 1.9.2 + %r{zeitwerk/(core_ext/)?kernel\.rb} # Zeitwerk kernel#require decorator + ].freeze + + attr_reader :routes, :route_sets, :filters, :templates, :errors, :on_start_callback, :on_stop_callback + + def callers_to_ignore + CALLERS_TO_IGNORE + end + + # Removes all routes, filters, middleware and extension hooks from the + # current class (not routes/filters/... defined by its superclass). + def reset! + @conditions = [] + @routes = {} + @route_sets = {} + @filters = { before: [], after: [] } + @errors = {} + @middleware = [] + @prototype = nil + @extensions = [] + + @templates = if superclass.respond_to?(:templates) + Hash.new { |_hash, key| superclass.templates[key] } + else + {} + end + end + + # Extension modules registered on this class and all superclasses. + def extensions + if superclass.respond_to?(:extensions) + (@extensions + superclass.extensions).uniq + else + @extensions + end + end + + # Middleware used in this class and all superclasses. + def middleware + if superclass.respond_to?(:middleware) + superclass.middleware + @middleware + else + @middleware + end + end + + # Sets an option to the given value. If the value is a proc, + # the proc will be called every time the option is accessed. + def set(option, value = (not_set = true), ignore_setter = false, &block) + raise ArgumentError if block && !not_set + + if block + value = block + not_set = false + end + + if not_set + raise ArgumentError unless option.respond_to?(:each) + + option.each { |k, v| set(k, v) } + return self + end + + if respond_to?("#{option}=") && !ignore_setter + return __send__("#{option}=", value) + end + + setter = proc { |val| set option, val, true } + getter = proc { value } + + case value + when Proc + getter = value + when Symbol, Integer, FalseClass, TrueClass, NilClass + getter = value.inspect + when Hash + setter = proc do |val| + val = value.merge val if Hash === val + set option, val, true + end + end + + define_singleton("#{option}=", setter) + define_singleton(option, getter) + define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?" + self + end + + # Same as calling `set :option, true` for each of the given options. + def enable(*opts) + opts.each { |key| set(key, true) } + end + + # Same as calling `set :option, false` for each of the given options. + def disable(*opts) + opts.each { |key| set(key, false) } + end + + # Define a custom error handler. Optionally takes either an Exception + # class, or an HTTP status code to specify which errors should be + # handled. + def error(*codes, &block) + args = compile! 'ERROR', /.*/, block + codes = codes.flat_map(&method(:Array)) + codes << Exception if codes.empty? + codes << Sinatra::NotFound if codes.include?(404) + codes.each { |c| (@errors[c] ||= []) << args } + end + + # Sugar for `error(404) { ... }` + def not_found(&block) + error(404, &block) + end + + # Define a named template. The block must return the template source. + def template(name, &block) + filename, line = caller_locations.first + templates[name] = [block, filename, line.to_i] + end + + # Define the layout template. The block must return the template source. + def layout(name = :layout, &block) + template name, &block + end + + # Load embedded templates from the file; uses the caller's __FILE__ + # when no file is specified. + def inline_templates=(file = nil) + file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true + + begin + io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file) + app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2) + rescue Errno::ENOENT + app, data = nil + end + + return unless data + + encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m + $2 + else + settings.default_encoding + end + + lines = app.count("\n") + 1 + template = nil + force_encoding data, encoding + data.each_line do |line| + lines += 1 + if line =~ /^@@\s*(.*\S)\s*$/ + template = force_encoding(String.new, encoding) + templates[$1.to_sym] = [template, file, lines] + elsif template + template << line + end + end + end + + # Lookup or register a mime type in Rack's mime registry. + def mime_type(type, value = nil) + return type if type.nil? + return type.to_s if type.to_s.include?('/') + + type = ".#{type}" unless type.to_s[0] == '.' + return Rack::Mime.mime_type(type, nil) unless value + + Rack::Mime::MIME_TYPES[type] = value + end + + # provides all mime types matching type, including deprecated types: + # mime_types :html # => ['text/html'] + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type + if type =~ %r{^application/(xml|javascript)$} + [type, "text/#{$1}"] + elsif type =~ %r{^text/(xml|javascript)$} + [type, "application/#{$1}"] + else + [type] + end + end + + # Define a before filter; runs before all requests within the same + # context as route handlers and may access/modify the request and + # response. + def before(path = /.*/, **options, &block) + add_filter(:before, path, **options, &block) + end + + # Define an after filter; runs after all requests within the same + # context as route handlers and may access/modify the request and + # response. + def after(path = /.*/, **options, &block) + add_filter(:after, path, **options, &block) + end + + # add a filter + def add_filter(type, path = /.*/, **options, &block) + filters[type] << compile!(type, path, block, **options) + end + + def on_start(&on_start_callback) + @on_start_callback = on_start_callback + end + + def on_stop(&on_stop_callback) + @on_stop_callback = on_stop_callback + end + + # Add a route condition. The route is considered non-matching when the + # block returns false. + def condition(name = "#{caller.first[/`.*'/]} condition", &block) + @conditions << generate_method(name, &block) + end + + def public=(value) + warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead' + set(:public_folder, value) + end + + def public_dir=(value) + self.public_folder = value + end + + def public_dir + public_folder + end + + # Defining a `GET` handler also automatically defines + # a `HEAD` handler. + def get(path, opts = {}, &block) + conditions = @conditions.dup + route('GET', path, opts, &block) + + @conditions = conditions + route('HEAD', path, opts, &block) + end + + def put(path, opts = {}, &block) route 'PUT', path, opts, &block end + + def post(path, opts = {}, &block) route 'POST', path, opts, &block end + + def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end + + def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end + + def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end + + def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end + + def link(path, opts = {}, &block) route 'LINK', path, opts, &block end + + def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end + + # Makes the methods defined in the block and in the Modules given + # in `extensions` available to the handlers and templates + def helpers(*extensions, &block) + class_eval(&block) if block_given? + include(*extensions) if extensions.any? + end + + # Register an extension. Alternatively take a block from which an + # extension will be created and registered on the fly. + def register(*extensions, &block) + extensions << Module.new(&block) if block_given? + @extensions += extensions + extensions.each do |extension| + extend extension + extension.registered(self) if extension.respond_to?(:registered) + end + end + + def development?; environment == :development end + def production?; environment == :production end + def test?; environment == :test end + + # Set configuration options for Sinatra and/or the app. + # Allows scoping of settings for certain environments. + def configure(*envs) + yield self if envs.empty? || envs.include?(environment.to_sym) + end + + # Use the specified Rack middleware + def use(middleware, *args, &block) + @prototype = nil + @middleware << [middleware, args, block] + end + ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true) + + # Stop the self-hosted server if running. + def quit! + return unless running? + + # Use Thin's hard #stop! if available, otherwise just #stop. + running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop + warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages? + set :running_server, nil + set :handler_name, nil + + on_stop_callback.call unless on_stop_callback.nil? + end + + alias stop! quit! + + # Run the Sinatra app as a self-hosted server using + # Puma, Falcon (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + unless defined?(Rackup::Handler) + rackup_warning = <<~MISSING_RACKUP + Sinatra could not start, the required gems weren't found! + + Add them to your bundle with: + + bundle add rackup puma + + or install them with: + + gem install rackup puma + + MISSING_RACKUP + warn rackup_warning + exit 1 + end + + return if running? + + set options + handler = Rackup::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(Port: port, Host: bind) + + begin + start_server(handler, server_settings, handler_name, &block) + rescue Errno::EADDRINUSE + warn "== Someone is already performing on port #{port}!" + raise + ensure + quit! + end + end + + alias start! run! + + # Check whether the self-hosted server is running or not. + def running? + running_server? + end + + # The prototype instance used to process requests. + def prototype + @prototype ||= new + end + + # Create a new instance without middleware in front of it. + alias new! new unless method_defined? :new! + + # Create a new instance of the class fronted by its middleware + # pipeline. The object is guaranteed to respond to #call but may not be + # an instance of the class new was called on. + def new(*args, &block) + instance = new!(*args, &block) + Wrapper.new(build(instance).to_app, instance) + end + ruby2_keywords :new if respond_to?(:ruby2_keywords, true) + + # Creates a Rack::Builder instance with all the middleware set up and + # the given +app+ as end point. + def build(app) + builder = Rack::Builder.new + setup_default_middleware builder + setup_middleware builder + builder.run app + builder + end + + def call(env) + synchronize { prototype.call(env) } + end + + # Like Kernel#caller but excluding certain magic entries and without + # line / method information; the resulting array contains filenames only. + def caller_files + cleaned_caller(1).flatten + end + + private + + # Starts the server by running the Rack Handler. + def start_server(handler, server_settings, handler_name) + # Ensure we initialize middleware before startup, to match standard Rack + # behavior, by ensuring an instance exists: + prototype + # Run the instance we created: + handler.run(self, **server_settings) do |server| + unless suppress_messages? + warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}" + end + + setup_traps + set :running_server, server + set :handler_name, handler_name + server.threaded = settings.threaded if server.respond_to? :threaded= + on_start_callback.call unless on_start_callback.nil? + yield server if block_given? + end + end + + def suppress_messages? + handler_name =~ /cgi/i || quiet + end + + def setup_traps + return unless traps? + + at_exit { quit! } + + %i[INT TERM].each do |signal| + old_handler = trap(signal) do + quit! + old_handler.call if old_handler.respond_to?(:call) + end + end + + set :traps, false + end + + # Dynamically defines a method on settings. + def define_singleton(name, content = Proc.new) + singleton_class.class_eval do + undef_method(name) if method_defined? name + String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content) + end + end + + # Condition for matching host name. Parameter might be String or Regexp. + def host_name(pattern) + condition { pattern === request.host } + end + + # Condition for matching user agent. Parameter should be Regexp. + # Will set params[:agent]. + def user_agent(pattern) + condition do + if request.user_agent.to_s =~ pattern + @params[:agent] = $~[1..-1] + true + else + false + end + end + end + alias agent user_agent + + # Condition for matching mimetypes. Accepts file extensions. + def provides(*types) + types.map! { |t| mime_types(t) } + types.flatten! + condition do + response_content_type = response['content-type'] + preferred_type = request.preferred_type(types) + + if response_content_type + types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/]) + elsif preferred_type + params = (preferred_type.respond_to?(:params) ? preferred_type.params : {}) + content_type(preferred_type, params) + true + else + false + end + end + end + + def route(verb, path, options = {}, &block) + enable :empty_path_info if path == '' && empty_path_info.nil? + pattern, _ = signature = compile!(verb, path, block, **options) + + (@routes[verb] ||= []) << signature + + if pattern.is_a? Mustermann::AST::Pattern + @route_sets[verb] = Mustermann::Set.new(strict_order: true) if @route_sets[verb].nil? + @route_sets[verb].add(pattern, signature) if @route_sets[verb] + else + @route_sets[verb] = false + end + + invoke_hook(:route_added, verb, path, block) + signature + end + + def invoke_hook(name, *args) + extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } + end + + def generate_method(method_name, &block) + define_method(method_name, &block) + method = instance_method method_name + remove_method method_name + method + end + + def compile!(verb, path, block, **options) + # Because of self.options.host + host_name(options.delete(:host)) if options.key?(:host) + # Pass Mustermann opts to compile() + route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze + + options.each_pair { |option, args| send(option, *args) } + + pattern = compile(path, route_mustermann_opts) + method_name = "#{verb} #{path}" + unbound_method = generate_method(method_name, &block) + conditions = @conditions + @conditions = [] + wrapper = block.arity.zero? ? + proc { |a, _p| unbound_method.bind(a).call } : + proc { |a, p| unbound_method.bind(a).call(*p) } + + [pattern, conditions, wrapper] + end + + def compile(path, route_mustermann_opts = {}) + Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts)) + end + + def setup_default_middleware(builder) + builder.use ExtendedRack + builder.use ShowExceptions if show_exceptions? + builder.use Rack::MethodOverride if method_override? + builder.use Rack::Head + setup_logging builder + setup_sessions builder + setup_protection builder + setup_host_authorization builder + end + + def setup_middleware(builder) + middleware.each { |c, a, b| builder.use(c, *a, &b) } + end + + def setup_logging(builder) + if logging? + setup_common_logger(builder) + setup_custom_logger(builder) + elsif logging == false + setup_null_logger(builder) + end + end + + def setup_null_logger(builder) + builder.use Sinatra::Middleware::Logger, ::Logger::FATAL + end + + def setup_common_logger(builder) + builder.use Sinatra::CommonLogger + end + + def setup_custom_logger(builder) + if logging.respond_to? :to_int + builder.use Sinatra::Middleware::Logger, logging + else + builder.use Sinatra::Middleware::Logger + end + end + + def setup_protection(builder) + return unless protection? + + options = Hash === protection ? protection.dup : {} + options = { + img_src: "'self' data:", + font_src: "'self'" + }.merge options + + protect_session = options.fetch(:session) { sessions? } + options[:without_session] = !protect_session + + options[:reaction] ||= :drop_session + + builder.use Rack::Protection, options + end + + def setup_host_authorization(builder) + builder.use Rack::Protection::HostAuthorization, host_authorization + end + + def setup_sessions(builder) + return unless sessions? + + options = {} + options[:secret] = session_secret if session_secret? + options.merge! sessions.to_hash if sessions.respond_to? :to_hash + builder.use session_store, options + end + + def inherited(subclass) + subclass.reset! + subclass.set :app_file, caller_files.first unless subclass.app_file? + super + end + + @@mutex = Mutex.new + def synchronize(&block) + if lock? + @@mutex.synchronize(&block) + else + yield + end + end + + # used for deprecation warnings + def warn_for_deprecation(message) + warn message + "\n\tfrom #{cleaned_caller.first.join(':')}" + end + + # Like Kernel#caller but excluding certain magic entries + def cleaned_caller(keep = 3) + caller(1) + .map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] } + .reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } } + end + end + + # Force data to specified encoding. It defaults to settings.default_encoding + # which is UTF-8 by default + def self.force_encoding(data, encoding = default_encoding) + return if data == settings || data.is_a?(Tempfile) + + if data.respond_to? :force_encoding + data.force_encoding(encoding).encode! + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v, encoding) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v, encoding) } + end + data + end + + def force_encoding(*args) + settings.force_encoding(*args) + end + + reset! + + set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym + set :raise_errors, proc { test? } + set :dump_errors, proc { !test? } + set :show_exceptions, proc { development? } + set :sessions, false + set :session_store, Rack::Session::Cookie + set :logging, false + set :protection, true + set :method_override, false + set :use_code, false + set :default_encoding, 'utf-8' + set :x_cascade, true + set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" } + settings.add_charset << %r{^text/} + set :mustermann_opts, {} + set :default_content_type, 'text/html' + + # explicitly generating a session secret eagerly to play nice with preforking + begin + require 'securerandom' + set :session_secret, SecureRandom.hex(64) + rescue LoadError, NotImplementedError, RuntimeError + # SecureRandom raises a NotImplementedError if no random device is available + # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230 + set :session_secret, format('%064x', Kernel.rand((2**256) - 1)) + end + + class << self + alias methodoverride? method_override? + alias methodoverride= method_override= + end + + set :run, false # start server via at-exit hook? + set :running_server, nil + set :handler_name, nil + set :traps, true + set :server, %w[webrick] + set :bind, proc { development? ? 'localhost' : '0.0.0.0' } + set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567) + set :quiet, false + set :host_authorization, ->() do + if development? + { + permitted_hosts: [ + "localhost", + ".localhost", + ".test", + IPAddr.new("0.0.0.0/0"), + IPAddr.new("::/0"), + ] + } + else + {} + end + end + + ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'falcon' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + server.unshift 'puma' + + set :absolute_redirects, true + set :prefixed_redirects, false + set :empty_path_info, nil + set :strict_paths, true + + set :app_file, nil + set :root, proc { app_file && File.expand_path(File.dirname(app_file)) } + set :views, proc { root && File.join(root, 'views') } + set :reload_templates, proc { development? } + set :lock, false + set :threaded, true + + set :public_folder, proc { root && File.join(root, 'public') } + set :static, proc { public_folder && File.exist?(public_folder) } + set :static_cache_control, false + + set :static_headers, {} + + error ::Exception do + response.status = 500 + content_type 'text/html' + '

Internal Server Error

' + end + + configure :development do + get '/__sinatra__/:image.png' do + filename = __dir__ + "/images/#{params[:image].to_i}.png" + content_type :png + send_file filename + end + + error NotFound do + content_type 'text/html' + + if instance_of?(Sinatra::Application) + code = <<-RUBY.gsub(/^ {12}/, '') + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + RUBY + else + code = <<-RUBY.gsub(/^ {12}/, '') + class #{self.class} + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + end + RUBY + + file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '') + code = "# in #{file}\n#{code}" unless file.empty? + end + + <<-HTML.gsub(/^ {10}/, '') + + + + + + +

Sinatra doesn’t know this ditty.

+ +
+ Try this: +
#{Rack::Utils.escape_html(code)}
+
+ + + HTML + end + end + end + + # Execution context for classic style (top-level) applications. All + # DSL methods executed on main are delegated to this class. + # + # The Application class should not be subclassed, unless you want to + # inherit all settings, routes, handlers, and error pages from the + # top-level. Subclassing Sinatra::Base is highly recommended for + # modular applications. + class Application < Base + set :logging, proc { !test? } + set :method_override, true + set :run, proc { !test? } + set :app_file, nil + + def self.register(*extensions, &block) # :nodoc: + added_methods = extensions.flat_map(&:public_instance_methods) + Delegator.delegate(*added_methods) + super(*extensions, &block) + end + end + + # Sinatra delegation mixin. Mixing this module into an object causes all + # methods to be delegated to the Sinatra::Application class. Used primarily + # at the top-level. + module Delegator # :nodoc: + def self.delegate(*methods) + methods.each do |method_name| + next if method_defined?(method_name) || private_method_defined?(method_name) + + define_method(method_name) do |*args, &block| + return super(*args, &block) if respond_to? method_name + + Delegator.target.send(method_name, *args, &block) + end + # ensure keyword argument passing is compatible with ruby >= 2.7 + ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true) + private method_name + end + end + + delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink, + :template, :layout, :before, :after, :error, :not_found, :configure, + :set, :mime_type, :enable, :disable, :use, :development?, :test?, + :production?, :helpers, :settings, :register, :on_start, :on_stop + + class << self + attr_accessor :target + end + + self.target = Application + end + + class Wrapper + def initialize(stack, instance) + @stack = stack + @instance = instance + end + + def settings + @instance.settings + end + + def helpers + @instance + end + + def call(env) + @stack.call(env) + end + + def inspect + "#<#{@instance.class} app_file=#{settings.app_file.inspect}>" + end + end + + # Create a new Sinatra application; the block is evaluated in the class scope. + def self.new(base = Base, &block) + base = Class.new(base) + base.class_eval(&block) if block_given? + base + end + + # Extend the top-level DSL with the modules provided. + def self.register(*extensions, &block) + Delegator.target.register(*extensions, &block) + end + + # Include the helper modules provided in Sinatra's request context. + def self.helpers(*extensions, &block) + Delegator.target.helpers(*extensions, &block) + end + + # Use the middleware for classic applications. + def self.use(*args, &block) + Delegator.target.use(*args, &block) + end +end diff --git a/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/old.rb b/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/old.rb new file mode 100644 index 0000000..4a54a2d --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_content_type_leak_in_muste_4/old.rb @@ -0,0 +1,2207 @@ +# frozen_string_literal: true + +# external dependencies +require 'rack' +begin + require 'rackup' +rescue LoadError +end +require 'tilt' +require 'rack/protection' +require 'rack/session' +require 'mustermann' +require 'mustermann/set' +require 'mustermann/sinatra' +require 'mustermann/regular' + +# stdlib dependencies +require 'ipaddr' +require 'time' +require 'uri' + +# other files we need +require 'sinatra/indifferent_hash' +require 'sinatra/show_exceptions' +require 'sinatra/version' + +require_relative 'middleware/logger' + +module Sinatra + # The request object. See Rack::Request for more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Request + class Request < Rack::Request + HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze + HEADER_VALUE_WITH_PARAMS = %r{(?:(?:\w+|\*)/(?:\w+(?:\.|-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*}.freeze + + # Returns an array of acceptable media types for the response + def accept + @env['sinatra.accept'] ||= if @env.include?('HTTP_ACCEPT') && (@env['HTTP_ACCEPT'].to_s != '') + @env['HTTP_ACCEPT'] + .to_s + .scan(HEADER_VALUE_WITH_PARAMS) + .map! { |e| AcceptEntry.new(e) } + .sort + else + [AcceptEntry.new('*/*')] + end + end + + def accept?(type) + preferred_type(type).to_s.include?(type) + end + + def preferred_type(*types) + return accept.first if types.empty? + + types.flatten! + return types.first if accept.empty? + + accept.detect do |accept_header| + type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) } + return type if type + end + end + + alias secure? ssl? + + def forwarded? + !forwarded_authority.nil? + end + + def safe? + get? || head? || options? || trace? + end + + def idempotent? + safe? || put? || delete? || link? || unlink? + end + + def link? + request_method == 'LINK' + end + + def unlink? + request_method == 'UNLINK' + end + + def params + super + rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e + raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}" + rescue EOFError => e + raise BadRequest, "Invalid multipart/form-data: #{Rack::Utils.escape_html(e.message)}" + end + + class AcceptEntry + attr_accessor :params + attr_reader :entry + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @entry = entry + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + @q = @params.delete('q') { 1.0 }.to_f + end + + def <=>(other) + other.priority <=> priority + end + + def priority + # We sort in descending order; better matches should be higher. + [@q, -@type.count('*'), @params.size] + end + + def to_str + @type + end + + def to_s(full = false) + full ? entry : to_str + end + + def respond_to?(*args) + super || to_str.respond_to?(*args) + end + + def method_missing(*args, &block) + to_str.send(*args, &block) + end + end + + class MimeTypeEntry + attr_reader :params + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + end + + def accepts?(entry) + File.fnmatch(entry, self) && matches_params?(entry.params) + end + + def to_str + @type + end + + def matches_params?(params) + return true if @params.empty? + + params.all? { |k, v| !@params.key?(k) || @params[k] == v } + end + end + end + + # The response object. See Rack::Response and Rack::Response::Helpers for + # more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Response + # https://rubydoc.info/github/rack/rack/main/Rack/Response/Helpers + class Response < Rack::Response + DROP_BODY_RESPONSES = [204, 304].freeze + + def body=(value) + value = value.body while Rack::Response === value + @body = String === value ? [value.to_str] : value + end + + def each + block_given? ? super : enum_for(:each) + end + + def finish + result = body + + if drop_content_info? + headers.delete 'content-length' + headers.delete 'content-type' + end + + if drop_body? + close + result = [] + end + + if calculate_content_length? + # if some other code has already set content-length, don't muck with it + # currently, this would be the static file-handler + headers['content-length'] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] + end + + private + + def calculate_content_length? + headers['content-type'] && !headers['content-length'] && (Array === body) + end + + def drop_content_info? + informational? || drop_body? + end + + def drop_body? + DROP_BODY_RESPONSES.include?(status) + end + end + + # Some Rack handlers implement an extended body object protocol, however, + # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question. + # This middleware will detect an extended body object and will make sure it reaches the + # handler directly. We do this here, so our middleware and middleware set up by the app will + # still be able to run. + class ExtendedRack < Struct.new(:app) + def call(env) + result = app.call(env) + callback = env['async.callback'] + return result unless callback && async?(*result) + + after_response { callback.call result } + setup_close(env, *result) + throw :async + end + + private + + def setup_close(env, _status, _headers, body) + return unless body.respond_to?(:close) && env.include?('async.close') + + env['async.close'].callback { body.close } + env['async.close'].errback { body.close } + end + + def after_response(&block) + raise NotImplementedError, 'only supports EventMachine at the moment' unless defined? EventMachine + + EventMachine.next_tick(&block) + end + + def async?(status, _headers, body) + return true if status == -1 + + body.respond_to?(:callback) && body.respond_to?(:errback) + end + end + + # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing, + # if another CommonLogger is already in the middleware chain. + class CommonLogger < Rack::CommonLogger + def call(env) + env['sinatra.commonlogger'] ? @app.call(env) : super + end + + superclass.class_eval do + alias_method :call_without_check, :call unless method_defined? :call_without_check + def call(env) + env['sinatra.commonlogger'] = true + call_without_check(env) + end + end + end + + class Error < StandardError # :nodoc: + end + + class BadRequest < Error # :nodoc: + def http_status; 400 end + end + + class NotFound < Error # :nodoc: + def http_status; 404 end + end + + # Methods available to routes, before/after filters, and views. + module Helpers + # Set or retrieve the response status code. + def status(value = nil) + response.status = Rack::Utils.status_code(value) if value + response.status + end + + # Set or retrieve the response body. When a block is given, + # evaluation is deferred until the body is read with #each. + def body(value = nil, &block) + if block_given? + def block.each; yield(call) end + response.body = block + elsif value + unless request.head? || value.is_a?(Rack::Files::BaseIterator) || value.is_a?(Stream) + headers.delete 'content-length' + end + response.body = value + else + response.body + end + end + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) + http_version = env['SERVER_PROTOCOL'] + if (http_version == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') + status 303 + else + status 302 + end + + # According to RFC 2616 section 14.30, "the field value consists of a + # single absolute URI" + response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?) + halt(*args) + end + + # Generates the absolute URI for a given path in the app. + # Takes Rack routers and reverse proxies into account. + def uri(addr = nil, absolute = true, add_script_name = true) + return addr if addr.to_s =~ /\A[a-z][a-z0-9+.\-]*:/i + + uri = [host = String.new] + if absolute + host << "http#{'s' if request.secure?}://" + host << if request.forwarded? || (request.port != (request.secure? ? 443 : 80)) + request.host_with_port + else + request.host + end + end + uri << request.script_name.to_s if add_script_name + uri << (addr || request.path_info).to_s + File.join uri + end + + alias url uri + alias to uri + + # Halt processing and return the error status provided. + def error(code, body = nil) + if code.respond_to? :to_str + body = code.to_str + code = 500 + end + response.body = body unless body.nil? + halt code + end + + # Halt processing and return a 404 Not Found. + def not_found(body = nil) + error 404, body + end + + # Set multiple response headers with Hash. + def headers(hash = nil) + response.headers.merge! hash if hash + response.headers + end + + # Access the underlying Rack session. + def session + request.session + end + + # Access shared logger object. + def logger + request.logger + end + + # Look up a media type by file extension in Rack's mime registry. + def mime_type(type) + Base.mime_type(type) + end + + # Set the content-type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) + return response['content-type'] unless type + + default = params.delete :default + mime_type = mime_type(type) || default + raise format('Unknown media type: %p', type) if mime_type.nil? + + mime_type = mime_type.dup + unless params.include?(:charset) || settings.add_charset.all? { |p| !(p === mime_type) } + params[:charset] = params.delete('charset') || settings.default_encoding + end + params.delete :charset if mime_type.include? 'charset' + unless params.empty? + mime_type << ';' + mime_type << params.map do |key, val| + val = val.inspect if val.to_s =~ /[";,]/ + "#{key}=#{val}" + end.join(';') + end + response['content-type'] = mime_type + end + + # https://html.spec.whatwg.org/#multipart-form-data + MULTIPART_FORM_DATA_REPLACEMENT_TABLE = { + '"' => '%22', + "\r" => '%0D', + "\n" => '%0A' + }.freeze + + # Set the Content-Disposition to "attachment" with the specified filename, + # instructing the user agents to prompt to save. + def attachment(filename = nil, disposition = :attachment) + response['Content-Disposition'] = disposition.to_s.dup + return unless filename + + params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)) + response['Content-Disposition'] << params + ext = File.extname(filename) + content_type(ext) unless response['content-type'] || ext.empty? + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) + if opts[:type] || !response['content-type'] + content_type opts[:type] || File.extname(path), default: 'application/octet-stream' + end + + disposition = opts[:disposition] + filename = opts[:filename] + disposition = :attachment if disposition.nil? && filename + filename = path if filename.nil? + attachment(filename, disposition) if disposition + + last_modified opts[:last_modified] if opts[:last_modified] + + file = Rack::Files.new(File.dirname(settings.app_file)) + result = file.serving(request, path) + + result[1].each { |k, v| headers[k] ||= v } + headers['content-length'] = result[1]['content-length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT + not_found + end + + # Class of the response body in case you use #stream. + # + # Three things really matter: The front and back block (back being the + # block generating content, front the one sending it to the client) and + # the scheduler, integrating with whatever concurrency feature the Rack + # handler is using. + # + # Scheduler has to respond to defer and schedule. + class Stream + def self.schedule(*) yield end + def self.defer(*) yield end + + def initialize(scheduler = self.class, keep_open = false, &back) + @back = back.to_proc + @scheduler = scheduler + @keep_open = keep_open + @callbacks = [] + @closed = false + end + + def close + return if closed? + + @closed = true + @scheduler.schedule { @callbacks.each { |c| c.call } } + end + + def each(&front) + @front = front + @scheduler.defer do + begin + @back.call(self) + rescue Exception => e + @scheduler.schedule { raise e } + ensure + close unless @keep_open + end + end + end + + def <<(data) + @scheduler.schedule { @front.call(data.to_s) } + self + end + + def callback(&block) + return yield if closed? + + @callbacks << block + end + + alias errback callback + + def closed? + @closed + end + end + + # Allows to start sending data to the client even though later parts of + # the response body have not yet been generated. + # + # The close parameter specifies whether Stream#close should be called + # after the block has been executed. + def stream(keep_open = false) + scheduler = env['async.callback'] ? EventMachine : Stream + current = @params.dup + stream = if scheduler == Stream && keep_open + Stream.new(scheduler, false) do |out| + until out.closed? + with_params(current) { yield(out) } + end + end + else + Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } + end + body stream + end + + # Specify response freshness policy for HTTP caches (Cache-Control header). + # Any number of non-value directives (:public, :private, :no_cache, + # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with + # a Hash of value directives (:max_age, :s_maxage). + # + # cache_control :public, :must_revalidate, :max_age => 60 + # => Cache-Control: public, must-revalidate, max-age=60 + # + # See RFC 2616 / 14.9 for more on standard cache control directives: + # http://tools.ietf.org/html/rfc2616#section-14.9.1 + def cache_control(*values) + if values.last.is_a?(Hash) + hash = values.pop + hash.reject! { |_k, v| v == false } + hash.reject! { |k, v| values << k if v == true } + else + hash = {} + end + + values.map! { |value| value.to_s.tr('_', '-') } + hash.each do |key, value| + key = key.to_s.tr('_', '-') + value = value.to_i if %w[max-age s-maxage].include? key + values << "#{key}=#{value}" + end + + response['Cache-Control'] = values.join(', ') if values.any? + end + + # Set the Expires header and Cache-Control/max-age directive. Amount + # can be an integer number of seconds in the future or a Time object + # indicating when the response should be considered "stale". The remaining + # "values" arguments are passed to the #cache_control helper: + # + # expires 500, :public, :must_revalidate + # => Cache-Control: public, must-revalidate, max-age=500 + # => Expires: Mon, 08 Jun 2009 08:50:17 GMT + # + def expires(amount, *values) + values << {} unless values.last.is_a?(Hash) + + if amount.is_a? Integer + time = Time.now + amount.to_i + max_age = amount + else + time = time_for amount + max_age = time - Time.now + end + + values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 } + cache_control(*values) + + response['Expires'] = time.httpdate + end + + # Set the last modified time of the resource (HTTP 'Last-Modified' header) + # and halt if conditional GET matches. The +time+ argument is a Time, + # DateTime, or other object that responds to +to_time+. + # + # When the current request includes an 'If-Modified-Since' header that is + # equal or later than the time specified, execution is immediately halted + # with a '304 Not Modified' response. + def last_modified(time) + return unless time + + time = time_for time + response['Last-Modified'] = time.httpdate + return if env['HTTP_IF_NONE_MATCH'] + + if (status == 200) && env['HTTP_IF_MODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i + halt 304 if since >= time.to_i + end + + if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i + halt 412 if since < time.to_i + end + rescue ArgumentError + end + + ETAG_KINDS = %i[strong weak].freeze + # Set the response entity tag (HTTP 'ETag' header) and halt if conditional + # GET matches. The +value+ argument is an identifier that uniquely + # identifies the current version of the resource. The +kind+ argument + # indicates whether the etag should be used as a :strong (default) or :weak + # cache validator. + # + # When the current request includes an 'If-None-Match' header with a + # matching etag, execution is immediately halted. If the request method is + # GET or HEAD, a '304 Not Modified' response is sent. + def etag(value, options = {}) + # Before touching this code, please double check RFC 2616 14.24 and 14.26. + options = { kind: options } unless Hash === options + kind = options[:kind] || :strong + new_resource = options.fetch(:new_resource) { request.post? } + + unless ETAG_KINDS.include?(kind) + raise ArgumentError, ':strong or :weak expected' + end + + value = format('"%s"', value) + value = "W/#{value}" if kind == :weak + response['ETag'] = value + + return unless success? || status == 304 + + if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource) + halt(request.safe? ? 304 : 412) + end + + if env['HTTP_IF_MATCH'] + return if etag_matches?(env['HTTP_IF_MATCH'], new_resource) + + halt 412 + end + + nil + end + + # Sugar for redirect (example: redirect back) + def back + request.referer + end + + # whether or not the status is set to 1xx + def informational? + status.between? 100, 199 + end + + # whether or not the status is set to 2xx + def success? + status.between? 200, 299 + end + + # whether or not the status is set to 3xx + def redirect? + status.between? 300, 399 + end + + # whether or not the status is set to 4xx + def client_error? + status.between? 400, 499 + end + + # whether or not the status is set to 5xx + def server_error? + status.between? 500, 599 + end + + # whether or not the status is set to 404 + def not_found? + status == 404 + end + + # whether or not the status is set to 400 + def bad_request? + status == 400 + end + + # Generates a Time object from the given value. + # Used by #expires and #last_modified. + def time_for(value) + if value.is_a? Numeric + Time.at value + elsif value.respond_to? :to_s + Time.parse value.to_s + else + value.to_time + end + rescue ArgumentError => e + raise e + rescue Exception + raise ArgumentError, "unable to convert #{value.inspect} to a Time object" + end + + private + + # Helper method checking if a ETag value list includes the current ETag. + def etag_matches?(list, new_resource = request.post?) + return !new_resource if list == '*' + + list.to_s.split(',').map(&:strip).include?(response['ETag']) + end + + def with_params(temp_params) + original = @params + @params = temp_params + yield + ensure + @params = original if original + end + end + + # Template rendering methods. Each method takes the name of a template + # to render as a Symbol and returns a String with the rendered output, + # as well as an optional hash with additional options. + # + # `template` is either the name or path of the template as symbol + # (Use `:'subdir/myview'` for views in subdirectories), or a string + # that will be rendered. + # + # Possible options are: + # :content_type The content type to use, same arguments as content_type. + # :layout If set to something falsy, no layout is rendered, otherwise + # the specified layout is used (Ignored for `sass`) + # :layout_engine Engine to use for rendering the layout. + # :locals A hash with local variables that should be available + # in the template + # :scope If set, template is evaluate with the binding of the given + # object rather than the application instance. + # :views Views directory to use. + module Templates + module ContentTyped + attr_accessor :content_type + end + + def initialize + super + @default_layout = :layout + @preferred_extension = nil + end + + def erb(template, options = {}, locals = {}, &block) + render(:erb, template, options, locals, &block) + end + + def haml(template, options = {}, locals = {}, &block) + render(:haml, template, options, locals, &block) + end + + def sass(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :sass, template, options, locals + end + + def scss(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :scss, template, options, locals + end + + def builder(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:builder, template, options, locals, &block) + end + + def liquid(template, options = {}, locals = {}, &block) + render(:liquid, template, options, locals, &block) + end + + def markdown(template, options = {}, locals = {}) + options[:exclude_outvar] = true + render :markdown, template, options, locals + end + + def rdoc(template, options = {}, locals = {}) + render :rdoc, template, options, locals + end + + def asciidoc(template, options = {}, locals = {}) + render :asciidoc, template, options, locals + end + + def markaby(template = nil, options = {}, locals = {}, &block) + render_ruby(:mab, template, options, locals, &block) + end + + def nokogiri(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:nokogiri, template, options, locals, &block) + end + + def slim(template, options = {}, locals = {}, &block) + render(:slim, template, options, locals, &block) + end + + def yajl(template, options = {}, locals = {}) + options[:default_content_type] = :json + render :yajl, template, options, locals + end + + def rabl(template, options = {}, locals = {}) + Rabl.register! + render :rabl, template, options, locals + end + + # Calls the given block for every possible template file in views, + # named name.ext, where ext is registered on engine. + def find_template(views, name, engine) + yield ::File.join(views, "#{name}.#{@preferred_extension}") + + Tilt.default_mapping.extensions_for(engine).each do |ext| + yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension + end + end + + private + + # logic shared between builder and nokogiri + def render_ruby(engine, template, options = {}, locals = {}, &block) + if template.is_a?(Hash) + options = template + template = nil + end + template = proc { block } if template.nil? + render engine, template, options, locals + end + + def render(engine, data, options = {}, locals = {}, &block) + # merge app-level options + engine_options = settings.respond_to?(engine) ? settings.send(engine) : {} + options.merge!(engine_options) { |_key, v1, _v2| v1 } + + # extract generic options + locals = options.delete(:locals) || locals || {} + views = options.delete(:views) || settings.views || './views' + layout = options[:layout] + layout = false if layout.nil? && options.include?(:layout) + eat_errors = layout.nil? + layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false) + layout = @default_layout if layout.nil? || (layout == true) + layout_options = options.delete(:layout_options) || {} + content_type = options.delete(:default_content_type) + content_type = options.delete(:content_type) || content_type + layout_engine = options.delete(:layout_engine) || engine + scope = options.delete(:scope) || self + exclude_outvar = options.delete(:exclude_outvar) + options.delete(:layout) + + # set some defaults + options[:outvar] ||= '@_out_buf' unless exclude_outvar + options[:default_encoding] ||= settings.default_encoding + + # compile and render template + begin + layout_was = @default_layout + @default_layout = false + template = compile_template(engine, data, options, views) + output = template.render(scope, locals, &block) + ensure + @default_layout = layout_was + end + + # render layout + if layout + extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope } + options = options.merge(extra_options).merge!(layout_options) + + catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } } + end + + if content_type + # sass-embedded returns a frozen string + output = +output + output.extend(ContentTyped).content_type = content_type + end + output + end + + def compile_template(engine, data, options, views) + eat_errors = options.delete :eat_errors + template = Tilt[engine] + raise "Template engine not found: #{engine}" if template.nil? + + case data + when Symbol + template_cache.fetch engine, data, options, views do + body, path, line = settings.templates[data] + if body + body = body.call if body.respond_to?(:call) + template.new(path, line.to_i, options) { body } + else + found = false + @preferred_extension = engine.to_s + find_template(views, data, template) do |file| + path ||= file # keep the initial path rather than the last one + found = File.exist?(file) + if found + path = file + break + end + end + throw :layout_missing if eat_errors && !found + template.new(path, 1, options) + end + end + when Proc + compile_block_template(template, options, &data) + when String + template_cache.fetch engine, data, options, views do + compile_block_template(template, options) { data } + end + else + raise ArgumentError, "Sorry, don't know how to render #{data.inspect}." + end + end + + def compile_block_template(template, options, &body) + first_location = caller_locations.first + path = first_location.path + line = first_location.lineno + path = options[:path] || path + line = options[:line] || line + template.new(path, line.to_i, options, &body) + end + end + + # Extremely simple template cache implementation. + # * Not thread-safe. + # * Size is unbounded. + # * Keys are not copied defensively, and should not be modified after + # being passed to #fetch. More specifically, the values returned by + # key#hash and key#eql? should not change. + # + # Implementation copied from Tilt::Cache. + class TemplateCache + def initialize + @cache = {} + end + + # Caches a value for key, or returns the previously cached value. + # If a value has been previously cached for key then it is + # returned. Otherwise, block is yielded to and its return value + # which may be nil, is cached under key and returned. + def fetch(*key) + @cache.fetch(key) do + @cache[key] = yield + end + end + + # Clears the cache. + def clear + @cache = {} + end + end + + # Base class for all Sinatra applications and middleware. + class Base + include Rack::Utils + include Helpers + include Templates + + URI_INSTANCE = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new + + attr_accessor :app, :env, :request, :response, :params + attr_reader :template_cache + + def initialize(app = nil, **_kwargs) + super() + @app = app + @template_cache = TemplateCache.new + @pinned_response = nil # whether a before! filter pinned the content-type + yield self if block_given? + end + + # Rack call interface. + def call(env) + dup.call!(env) + end + + def call!(env) # :nodoc: + @env = env + @params = IndifferentHash.new + @request = Request.new(env) + @response = Response.new + @pinned_response = nil + template_cache.clear if settings.reload_templates + + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + + unless @response['content-type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif (default = settings.default_content_type) + content_type default + end + end + + @response.finish + end + + # Access settings defined with Base.set. + def self.settings + self + end + + # Access settings defined with Base.set. + def settings + self.class.settings + end + + # Exit the current block, halts any further processing + # of the request, and returns the specified response. + def halt(*response) + response = response.first if response.length == 1 + throw :halt, response + end + + # Pass control to the next matching route. + # If there are no more matching routes, Sinatra will + # return a 404 response. + def pass(&block) + throw :pass, block + end + + # Forward the request to the downstream app -- middleware only. + def forward + raise 'downstream app not set' unless @app.respond_to? :call + + status, headers, body = @app.call env + @response.status = status + @response.body = body + @response.headers.merge! headers + nil + end + + private + + # Run filters defined on the class and all superclasses. + # Accepts an optional block to call after each filter is applied. + def filter!(type, base = settings, &block) + filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters) + base.filters[type].each do |args| + result = process_route(*args) + block.call(result) if block_given? + end + end + + # Run routes defined on the class and all superclasses. + def route!(base = settings, pass_block = nil) + if set = base.route_sets[@request.request_method] + routes = set.match_all(route) + else + routes = base.routes[@request.request_method] + end + + routes&.each do |match_or_signature| + response.delete_header('content-type') unless @pinned_response + + if match_or_signature.is_a?(Mustermann::Match) + match = match_or_signature + pattern, conditions, block = match_or_signature.value + else + pattern, conditions, block = match_or_signature + end + + returned_pass_block = process_route(pattern, conditions, match: match) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" + route_eval { block[*args] } + end + + # don't wipe out pass_block in superclass + pass_block = returned_pass_block if returned_pass_block + end + + # Run routes defined in superclass. + if base.superclass.respond_to?(:routes) + return route!(base.superclass, pass_block) + end + + route_eval(&pass_block) if pass_block + route_missing + end + + # Run a route block and throw :halt with the result. + def route_eval + throw :halt, yield + end + + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, conditions, block = nil, values = [], match: nil) + if match + params = match.params + captures = match.captures + else + params = pattern.params(route) + return unless params + end + + params = params.dup + params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes + force_encoding(params) + + @params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any? + + regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) }) + if regexp_exists + match ||= pattern.match(route) + captures = match.captures.map { |c| URI_INSTANCE.unescape(c) if c } + values += captures + @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty? + else + values += params.values.flatten + end + + catch(:pass) do + conditions.each { |c| throw :pass if c.bind(self).call == false } + block ? block[self, values] : yield(self, values) + end + rescue StandardError + @env['sinatra.error.params'] = @params + raise + ensure + params ||= {} + params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params'] + end + + # The current route being processed. Used for pattern matching and URL generation. + def route + route = @request.path_info + route = '/' if route.empty? && !settings.empty_path_info? + route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/') + route + end + + # No matching route was found or all routes passed. The default + # implementation is to forward the request downstream when running + # as middleware (@app is non-nil); when no downstream app is set, raise + # a NotFound exception. Subclasses can override this method to perform + # custom route miss logic. + def route_missing + raise NotFound unless @app + + forward + end + + # Attempt to serve static files from public directory. Throws :halt when + # a matching file is found, returns nil otherwise. + # If custom static headers are defined, use them. + def static!(options = {}) + return if (public_dir = settings.public_folder).nil? + + path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" + return unless valid_path?(path) + + path = File.expand_path(path) + return unless path.start_with?("#{File.expand_path(public_dir)}/") + + return unless File.file?(path) + + env['sinatra.static_file'] = path + cache_control(*settings.static_cache_control) if settings.static_cache_control? + + headers(settings.static_headers) if settings.static_headers? + + send_file path, options.merge(disposition: nil) + end + + # Run the block with 'throw :halt' support and apply result to the response. + def invoke(&block) + res = catch(:halt, &block) + + res = [res] if (Integer === res) || (String === res) + if (Array === res) && (Integer === res.first) + res = res.dup + status(res.shift) + body(res.pop) + headers(*res) + elsif res.respond_to? :each + body res + end + nil # avoid double setting the same response tuple twice + end + + # Dispatch a request with error handling. + def dispatch! + # Avoid passing frozen string in force_encoding + @params.merge!(@request.params).each do |key, val| + next unless val.respond_to?(:force_encoding) + + val = val.dup if val.frozen? + @params[key] = force_encoding(val) + end + + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do + @pinned_response = !response['content-type'].nil? + end + route! + end + rescue ::Exception => e + invoke { handle_exception!(e) } + ensure + begin + filter! :after unless env['sinatra.static_file'] + rescue ::Exception => e + invoke { handle_exception!(e) } unless @env['sinatra.error'] + end + end + + # Error handling during requests. + def handle_exception!(boom) + error_params = @env['sinatra.error.params'] + + @params = @params.merge(error_params) if error_params + + @env['sinatra.error'] = boom + + http_status = if boom.is_a? Sinatra::Error + if boom.respond_to? :http_status + boom.http_status + elsif settings.use_code? && boom.respond_to?(:code) + boom.code + end + end + + http_status = 500 unless http_status&.between?(400, 599) + status(http_status) + + if server_error? + dump_errors! boom if settings.dump_errors? + raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler) + elsif not_found? + headers['X-Cascade'] = 'pass' if settings.x_cascade? + end + + if (res = error_block!(boom.class, boom) || error_block!(status, boom)) + return res + end + + if not_found? || bad_request? + if boom.message && boom.message != boom.class.name + body Rack::Utils.escape_html(boom.message) + else + content_type 'text/html' + body "

#{not_found? ? 'Not Found' : 'Bad Request'}

" + end + end + + return unless server_error? + + raise boom if settings.raise_errors? || settings.show_exceptions? + + error_block! Exception, boom + end + + # Find an custom error block for the key(s) specified. + def error_block!(key, *block_params) + base = settings + while base.respond_to?(:errors) + args_array = base.errors[key] + + next base = base.superclass unless args_array + + args_array.reverse_each do |args| + first = args == args_array.first + args += [block_params] + resp = process_route(*args) + return resp unless resp.nil? && !first + end + end + return false unless key.respond_to?(:superclass) && (key.superclass < Exception) + + error_block!(key.superclass, *block_params) + end + + def dump_errors!(boom) + if boom.respond_to?(:detailed_message) + msg = boom.detailed_message(highlight: false) + if msg =~ /\A(.*?)(?: \(#{ Regexp.quote(boom.class.to_s) }\))?\n/ + msg = $1 + additional_msg = $'.lines(chomp: true) + else + additional_msg = [] + end + else + msg = boom.message + additional_msg = [] + end + msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{msg}:", *additional_msg, *boom.backtrace].join("\n\t") + @env['rack.errors'].puts(msg) + end + + class << self + CALLERS_TO_IGNORE = [ # :nodoc: + %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code + %r{lib/tilt.*\.rb$}, # all tilt code + /^\(.*\)$/, # generated code + /\/bundled_gems.rb$/, # ruby >= 3.3 with bundler >= 2.5 + %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks + /active_support/, # active_support require hacks + %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks + /= 1.9.2 + %r{zeitwerk/(core_ext/)?kernel\.rb} # Zeitwerk kernel#require decorator + ].freeze + + attr_reader :routes, :route_sets, :filters, :templates, :errors, :on_start_callback, :on_stop_callback + + def callers_to_ignore + CALLERS_TO_IGNORE + end + + # Removes all routes, filters, middleware and extension hooks from the + # current class (not routes/filters/... defined by its superclass). + def reset! + @conditions = [] + @routes = {} + @route_sets = {} + @filters = { before: [], after: [] } + @errors = {} + @middleware = [] + @prototype = nil + @extensions = [] + + @templates = if superclass.respond_to?(:templates) + Hash.new { |_hash, key| superclass.templates[key] } + else + {} + end + end + + # Extension modules registered on this class and all superclasses. + def extensions + if superclass.respond_to?(:extensions) + (@extensions + superclass.extensions).uniq + else + @extensions + end + end + + # Middleware used in this class and all superclasses. + def middleware + if superclass.respond_to?(:middleware) + superclass.middleware + @middleware + else + @middleware + end + end + + # Sets an option to the given value. If the value is a proc, + # the proc will be called every time the option is accessed. + def set(option, value = (not_set = true), ignore_setter = false, &block) + raise ArgumentError if block && !not_set + + if block + value = block + not_set = false + end + + if not_set + raise ArgumentError unless option.respond_to?(:each) + + option.each { |k, v| set(k, v) } + return self + end + + if respond_to?("#{option}=") && !ignore_setter + return __send__("#{option}=", value) + end + + setter = proc { |val| set option, val, true } + getter = proc { value } + + case value + when Proc + getter = value + when Symbol, Integer, FalseClass, TrueClass, NilClass + getter = value.inspect + when Hash + setter = proc do |val| + val = value.merge val if Hash === val + set option, val, true + end + end + + define_singleton("#{option}=", setter) + define_singleton(option, getter) + define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?" + self + end + + # Same as calling `set :option, true` for each of the given options. + def enable(*opts) + opts.each { |key| set(key, true) } + end + + # Same as calling `set :option, false` for each of the given options. + def disable(*opts) + opts.each { |key| set(key, false) } + end + + # Define a custom error handler. Optionally takes either an Exception + # class, or an HTTP status code to specify which errors should be + # handled. + def error(*codes, &block) + args = compile! 'ERROR', /.*/, block + codes = codes.flat_map(&method(:Array)) + codes << Exception if codes.empty? + codes << Sinatra::NotFound if codes.include?(404) + codes.each { |c| (@errors[c] ||= []) << args } + end + + # Sugar for `error(404) { ... }` + def not_found(&block) + error(404, &block) + end + + # Define a named template. The block must return the template source. + def template(name, &block) + filename, line = caller_locations.first + templates[name] = [block, filename, line.to_i] + end + + # Define the layout template. The block must return the template source. + def layout(name = :layout, &block) + template name, &block + end + + # Load embedded templates from the file; uses the caller's __FILE__ + # when no file is specified. + def inline_templates=(file = nil) + file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true + + begin + io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file) + app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2) + rescue Errno::ENOENT + app, data = nil + end + + return unless data + + encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m + $2 + else + settings.default_encoding + end + + lines = app.count("\n") + 1 + template = nil + force_encoding data, encoding + data.each_line do |line| + lines += 1 + if line =~ /^@@\s*(.*\S)\s*$/ + template = force_encoding(String.new, encoding) + templates[$1.to_sym] = [template, file, lines] + elsif template + template << line + end + end + end + + # Lookup or register a mime type in Rack's mime registry. + def mime_type(type, value = nil) + return type if type.nil? + return type.to_s if type.to_s.include?('/') + + type = ".#{type}" unless type.to_s[0] == '.' + return Rack::Mime.mime_type(type, nil) unless value + + Rack::Mime::MIME_TYPES[type] = value + end + + # provides all mime types matching type, including deprecated types: + # mime_types :html # => ['text/html'] + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type + if type =~ %r{^application/(xml|javascript)$} + [type, "text/#{$1}"] + elsif type =~ %r{^text/(xml|javascript)$} + [type, "application/#{$1}"] + else + [type] + end + end + + # Define a before filter; runs before all requests within the same + # context as route handlers and may access/modify the request and + # response. + def before(path = /.*/, **options, &block) + add_filter(:before, path, **options, &block) + end + + # Define an after filter; runs after all requests within the same + # context as route handlers and may access/modify the request and + # response. + def after(path = /.*/, **options, &block) + add_filter(:after, path, **options, &block) + end + + # add a filter + def add_filter(type, path = /.*/, **options, &block) + filters[type] << compile!(type, path, block, **options) + end + + def on_start(&on_start_callback) + @on_start_callback = on_start_callback + end + + def on_stop(&on_stop_callback) + @on_stop_callback = on_stop_callback + end + + # Add a route condition. The route is considered non-matching when the + # block returns false. + def condition(name = "#{caller.first[/`.*'/]} condition", &block) + @conditions << generate_method(name, &block) + end + + def public=(value) + warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead' + set(:public_folder, value) + end + + def public_dir=(value) + self.public_folder = value + end + + def public_dir + public_folder + end + + # Defining a `GET` handler also automatically defines + # a `HEAD` handler. + def get(path, opts = {}, &block) + conditions = @conditions.dup + route('GET', path, opts, &block) + + @conditions = conditions + route('HEAD', path, opts, &block) + end + + def put(path, opts = {}, &block) route 'PUT', path, opts, &block end + + def post(path, opts = {}, &block) route 'POST', path, opts, &block end + + def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end + + def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end + + def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end + + def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end + + def link(path, opts = {}, &block) route 'LINK', path, opts, &block end + + def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end + + # Makes the methods defined in the block and in the Modules given + # in `extensions` available to the handlers and templates + def helpers(*extensions, &block) + class_eval(&block) if block_given? + include(*extensions) if extensions.any? + end + + # Register an extension. Alternatively take a block from which an + # extension will be created and registered on the fly. + def register(*extensions, &block) + extensions << Module.new(&block) if block_given? + @extensions += extensions + extensions.each do |extension| + extend extension + extension.registered(self) if extension.respond_to?(:registered) + end + end + + def development?; environment == :development end + def production?; environment == :production end + def test?; environment == :test end + + # Set configuration options for Sinatra and/or the app. + # Allows scoping of settings for certain environments. + def configure(*envs) + yield self if envs.empty? || envs.include?(environment.to_sym) + end + + # Use the specified Rack middleware + def use(middleware, *args, &block) + @prototype = nil + @middleware << [middleware, args, block] + end + ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true) + + # Stop the self-hosted server if running. + def quit! + return unless running? + + # Use Thin's hard #stop! if available, otherwise just #stop. + running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop + warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages? + set :running_server, nil + set :handler_name, nil + + on_stop_callback.call unless on_stop_callback.nil? + end + + alias stop! quit! + + # Run the Sinatra app as a self-hosted server using + # Puma, Falcon (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + unless defined?(Rackup::Handler) + rackup_warning = <<~MISSING_RACKUP + Sinatra could not start, the required gems weren't found! + + Add them to your bundle with: + + bundle add rackup puma + + or install them with: + + gem install rackup puma + + MISSING_RACKUP + warn rackup_warning + exit 1 + end + + return if running? + + set options + handler = Rackup::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(Port: port, Host: bind) + + begin + start_server(handler, server_settings, handler_name, &block) + rescue Errno::EADDRINUSE + warn "== Someone is already performing on port #{port}!" + raise + ensure + quit! + end + end + + alias start! run! + + # Check whether the self-hosted server is running or not. + def running? + running_server? + end + + # The prototype instance used to process requests. + def prototype + @prototype ||= new + end + + # Create a new instance without middleware in front of it. + alias new! new unless method_defined? :new! + + # Create a new instance of the class fronted by its middleware + # pipeline. The object is guaranteed to respond to #call but may not be + # an instance of the class new was called on. + def new(*args, &block) + instance = new!(*args, &block) + Wrapper.new(build(instance).to_app, instance) + end + ruby2_keywords :new if respond_to?(:ruby2_keywords, true) + + # Creates a Rack::Builder instance with all the middleware set up and + # the given +app+ as end point. + def build(app) + builder = Rack::Builder.new + setup_default_middleware builder + setup_middleware builder + builder.run app + builder + end + + def call(env) + synchronize { prototype.call(env) } + end + + # Like Kernel#caller but excluding certain magic entries and without + # line / method information; the resulting array contains filenames only. + def caller_files + cleaned_caller(1).flatten + end + + private + + # Starts the server by running the Rack Handler. + def start_server(handler, server_settings, handler_name) + # Ensure we initialize middleware before startup, to match standard Rack + # behavior, by ensuring an instance exists: + prototype + # Run the instance we created: + handler.run(self, **server_settings) do |server| + unless suppress_messages? + warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}" + end + + setup_traps + set :running_server, server + set :handler_name, handler_name + server.threaded = settings.threaded if server.respond_to? :threaded= + on_start_callback.call unless on_start_callback.nil? + yield server if block_given? + end + end + + def suppress_messages? + handler_name =~ /cgi/i || quiet + end + + def setup_traps + return unless traps? + + at_exit { quit! } + + %i[INT TERM].each do |signal| + old_handler = trap(signal) do + quit! + old_handler.call if old_handler.respond_to?(:call) + end + end + + set :traps, false + end + + # Dynamically defines a method on settings. + def define_singleton(name, content = Proc.new) + singleton_class.class_eval do + undef_method(name) if method_defined? name + String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content) + end + end + + # Condition for matching host name. Parameter might be String or Regexp. + def host_name(pattern) + condition { pattern === request.host } + end + + # Condition for matching user agent. Parameter should be Regexp. + # Will set params[:agent]. + def user_agent(pattern) + condition do + if request.user_agent.to_s =~ pattern + @params[:agent] = $~[1..-1] + true + else + false + end + end + end + alias agent user_agent + + # Condition for matching mimetypes. Accepts file extensions. + def provides(*types) + types.map! { |t| mime_types(t) } + types.flatten! + condition do + response_content_type = response['content-type'] + preferred_type = request.preferred_type(types) + + if response_content_type + types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/]) + elsif preferred_type + params = (preferred_type.respond_to?(:params) ? preferred_type.params : {}) + content_type(preferred_type, params) + true + else + false + end + end + end + + def route(verb, path, options = {}, &block) + enable :empty_path_info if path == '' && empty_path_info.nil? + pattern, _ = signature = compile!(verb, path, block, **options) + + (@routes[verb] ||= []) << signature + + if pattern.is_a? Mustermann::AST::Pattern + @route_sets[verb] = Mustermann::Set.new(strict_order: true) if @route_sets[verb].nil? + @route_sets[verb].add(pattern, signature) if @route_sets[verb] + else + @route_sets[verb] = false + end + + invoke_hook(:route_added, verb, path, block) + signature + end + + def invoke_hook(name, *args) + extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } + end + + def generate_method(method_name, &block) + define_method(method_name, &block) + method = instance_method method_name + remove_method method_name + method + end + + def compile!(verb, path, block, **options) + # Because of self.options.host + host_name(options.delete(:host)) if options.key?(:host) + # Pass Mustermann opts to compile() + route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze + + options.each_pair { |option, args| send(option, *args) } + + pattern = compile(path, route_mustermann_opts) + method_name = "#{verb} #{path}" + unbound_method = generate_method(method_name, &block) + conditions = @conditions + @conditions = [] + wrapper = block.arity.zero? ? + proc { |a, _p| unbound_method.bind(a).call } : + proc { |a, p| unbound_method.bind(a).call(*p) } + + [pattern, conditions, wrapper] + end + + def compile(path, route_mustermann_opts = {}) + Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts)) + end + + def setup_default_middleware(builder) + builder.use ExtendedRack + builder.use ShowExceptions if show_exceptions? + builder.use Rack::MethodOverride if method_override? + builder.use Rack::Head + setup_logging builder + setup_sessions builder + setup_protection builder + setup_host_authorization builder + end + + def setup_middleware(builder) + middleware.each { |c, a, b| builder.use(c, *a, &b) } + end + + def setup_logging(builder) + if logging? + setup_common_logger(builder) + setup_custom_logger(builder) + elsif logging == false + setup_null_logger(builder) + end + end + + def setup_null_logger(builder) + builder.use Sinatra::Middleware::Logger, ::Logger::FATAL + end + + def setup_common_logger(builder) + builder.use Sinatra::CommonLogger + end + + def setup_custom_logger(builder) + if logging.respond_to? :to_int + builder.use Sinatra::Middleware::Logger, logging + else + builder.use Sinatra::Middleware::Logger + end + end + + def setup_protection(builder) + return unless protection? + + options = Hash === protection ? protection.dup : {} + options = { + img_src: "'self' data:", + font_src: "'self'" + }.merge options + + protect_session = options.fetch(:session) { sessions? } + options[:without_session] = !protect_session + + options[:reaction] ||= :drop_session + + builder.use Rack::Protection, options + end + + def setup_host_authorization(builder) + builder.use Rack::Protection::HostAuthorization, host_authorization + end + + def setup_sessions(builder) + return unless sessions? + + options = {} + options[:secret] = session_secret if session_secret? + options.merge! sessions.to_hash if sessions.respond_to? :to_hash + builder.use session_store, options + end + + def inherited(subclass) + subclass.reset! + subclass.set :app_file, caller_files.first unless subclass.app_file? + super + end + + @@mutex = Mutex.new + def synchronize(&block) + if lock? + @@mutex.synchronize(&block) + else + yield + end + end + + # used for deprecation warnings + def warn_for_deprecation(message) + warn message + "\n\tfrom #{cleaned_caller.first.join(':')}" + end + + # Like Kernel#caller but excluding certain magic entries + def cleaned_caller(keep = 3) + caller(1) + .map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] } + .reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } } + end + end + + # Force data to specified encoding. It defaults to settings.default_encoding + # which is UTF-8 by default + def self.force_encoding(data, encoding = default_encoding) + return if data == settings || data.is_a?(Tempfile) + + if data.respond_to? :force_encoding + data.force_encoding(encoding).encode! + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v, encoding) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v, encoding) } + end + data + end + + def force_encoding(*args) + settings.force_encoding(*args) + end + + reset! + + set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym + set :raise_errors, proc { test? } + set :dump_errors, proc { !test? } + set :show_exceptions, proc { development? } + set :sessions, false + set :session_store, Rack::Session::Cookie + set :logging, false + set :protection, true + set :method_override, false + set :use_code, false + set :default_encoding, 'utf-8' + set :x_cascade, true + set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" } + settings.add_charset << %r{^text/} + set :mustermann_opts, {} + set :default_content_type, 'text/html' + + # explicitly generating a session secret eagerly to play nice with preforking + begin + require 'securerandom' + set :session_secret, SecureRandom.hex(64) + rescue LoadError, NotImplementedError, RuntimeError + # SecureRandom raises a NotImplementedError if no random device is available + # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230 + set :session_secret, format('%064x', Kernel.rand((2**256) - 1)) + end + + class << self + alias methodoverride? method_override? + alias methodoverride= method_override= + end + + set :run, false # start server via at-exit hook? + set :running_server, nil + set :handler_name, nil + set :traps, true + set :server, %w[webrick] + set :bind, proc { development? ? 'localhost' : '0.0.0.0' } + set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567) + set :quiet, false + set :host_authorization, ->() do + if development? + { + permitted_hosts: [ + "localhost", + ".localhost", + ".test", + IPAddr.new("0.0.0.0/0"), + IPAddr.new("::/0"), + ] + } + else + {} + end + end + + ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'falcon' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + server.unshift 'puma' + + set :absolute_redirects, true + set :prefixed_redirects, false + set :empty_path_info, nil + set :strict_paths, true + + set :app_file, nil + set :root, proc { app_file && File.expand_path(File.dirname(app_file)) } + set :views, proc { root && File.join(root, 'views') } + set :reload_templates, proc { development? } + set :lock, false + set :threaded, true + + set :public_folder, proc { root && File.join(root, 'public') } + set :static, proc { public_folder && File.exist?(public_folder) } + set :static_cache_control, false + + set :static_headers, {} + + error ::Exception do + response.status = 500 + content_type 'text/html' + '

Internal Server Error

' + end + + configure :development do + get '/__sinatra__/:image.png' do + filename = __dir__ + "/images/#{params[:image].to_i}.png" + content_type :png + send_file filename + end + + error NotFound do + content_type 'text/html' + + if instance_of?(Sinatra::Application) + code = <<-RUBY.gsub(/^ {12}/, '') + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + RUBY + else + code = <<-RUBY.gsub(/^ {12}/, '') + class #{self.class} + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + end + RUBY + + file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '') + code = "# in #{file}\n#{code}" unless file.empty? + end + + <<-HTML.gsub(/^ {10}/, '') + + + + + + +

Sinatra doesn’t know this ditty.

+ +
+ Try this: +
#{Rack::Utils.escape_html(code)}
+
+ + + HTML + end + end + end + + # Execution context for classic style (top-level) applications. All + # DSL methods executed on main are delegated to this class. + # + # The Application class should not be subclassed, unless you want to + # inherit all settings, routes, handlers, and error pages from the + # top-level. Subclassing Sinatra::Base is highly recommended for + # modular applications. + class Application < Base + set :logging, proc { !test? } + set :method_override, true + set :run, proc { !test? } + set :app_file, nil + + def self.register(*extensions, &block) # :nodoc: + added_methods = extensions.flat_map(&:public_instance_methods) + Delegator.delegate(*added_methods) + super(*extensions, &block) + end + end + + # Sinatra delegation mixin. Mixing this module into an object causes all + # methods to be delegated to the Sinatra::Application class. Used primarily + # at the top-level. + module Delegator # :nodoc: + def self.delegate(*methods) + methods.each do |method_name| + next if method_defined?(method_name) || private_method_defined?(method_name) + + define_method(method_name) do |*args, &block| + return super(*args, &block) if respond_to? method_name + + Delegator.target.send(method_name, *args, &block) + end + # ensure keyword argument passing is compatible with ruby >= 2.7 + ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true) + private method_name + end + end + + delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink, + :template, :layout, :before, :after, :error, :not_found, :configure, + :set, :mime_type, :enable, :disable, :use, :development?, :test?, + :production?, :helpers, :settings, :register, :on_start, :on_stop + + class << self + attr_accessor :target + end + + self.target = Application + end + + class Wrapper + def initialize(stack, instance) + @stack = stack + @instance = instance + end + + def settings + @instance.settings + end + + def helpers + @instance + end + + def call(env) + @stack.call(env) + end + + def inspect + "#<#{@instance.class} app_file=#{settings.app_file.inspect}>" + end + end + + # Create a new Sinatra application; the block is evaluated in the class scope. + def self.new(base = Base, &block) + base = Class.new(base) + base.class_eval(&block) if block_given? + base + end + + # Extend the top-level DSL with the modules provided. + def self.register(*extensions, &block) + Delegator.target.register(*extensions, &block) + end + + # Include the helper modules provided in Sinatra's request context. + def self.helpers(*extensions, &block) + Delegator.target.helpers(*extensions, &block) + end + + # Use the middleware for classic applications. + def self.use(*args, &block) + Delegator.target.use(*args, &block) + end +end diff --git a/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/expected.json b/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/expected.json new file mode 100644 index 0000000..b144fac --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/expected.json @@ -0,0 +1,991 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 22 + ], + "type": "comment", + "label": "# hash = Sinatra::IndifferentHash.new", + "start_byte": 848, + "end_byte": 887 + }, + "parent": { + "tree": "after", + "path": [ + 1 + ], + "type": "module", + "start_byte": 31, + "end_byte": 4620 + }, + "position": 22 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 23 + ], + "type": "comment", + "label": "# hash[:a] = 1", + "start_byte": 890, + "end_byte": 906 + }, + "parent": { + "tree": "after", + "path": [ + 1 + ], + "type": "module", + "start_byte": 31, + "end_byte": 4620 + }, + "position": 23 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 31 + ], + "type": "comment", + "label": "# hash = Sinatra::IndifferentHash", + "start_byte": 1063, + "end_byte": 1098 + }, + "parent": { + "tree": "after", + "path": [ + 1 + ], + "type": "module", + "start_byte": 31, + "end_byte": 4620 + }, + "position": 32 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 32 + ], + "type": "comment", + "label": "# hash[:a] = 1", + "start_byte": 1101, + "end_byte": 1117 + }, + "parent": { + "tree": "after", + "path": [ + 1 + ], + "type": "module", + "start_byte": 31, + "end_byte": 4620 + }, + "position": 33 + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 22 + ], + "type": "comment", + "label": "# hash = Sinatra::IndifferentHash.new(:a=\u003e1)", + "start_byte": 848, + "end_byte": 894 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 30 + ], + "type": "comment", + "label": "# hash = Sinatra::IndifferentHash.new(:a=\u003e1)", + "start_byte": 1051, + "end_byte": 1097 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": -1, + "right_line": 24 + }, + { + "left_line": 24, + "right_line": 25 + }, + { + "left_line": 25, + "right_line": 26 + }, + { + "left_line": 26, + "right_line": 27 + }, + { + "left_line": 27, + "right_line": 28 + }, + { + "left_line": 28, + "right_line": 29 + }, + { + "left_line": 29, + "right_line": 30 + }, + { + "left_line": 30, + "right_line": 31 + }, + { + "left_line": 31, + "right_line": 32 + }, + { + "left_line": -1, + "right_line": 33 + }, + { + "left_line": 32, + "right_line": 34 + }, + { + "left_line": 33, + "right_line": 35 + }, + { + "left_line": 34, + "right_line": 36 + }, + { + "left_line": 35, + "right_line": 37 + }, + { + "left_line": 36, + "right_line": 38 + }, + { + "left_line": 37, + "right_line": 39 + }, + { + "left_line": 38, + "right_line": 40 + }, + { + "left_line": 39, + "right_line": 41 + }, + { + "left_line": 40, + "right_line": 42 + }, + { + "left_line": 41, + "right_line": 43 + }, + { + "left_line": 42, + "right_line": 44 + }, + { + "left_line": 43, + "right_line": 45 + }, + { + "left_line": 44, + "right_line": 46 + }, + { + "left_line": 45, + "right_line": 47 + }, + { + "left_line": 46, + "right_line": 48 + }, + { + "left_line": 47, + "right_line": 49 + }, + { + "left_line": 48, + "right_line": 50 + }, + { + "left_line": 49, + "right_line": 51 + }, + { + "left_line": 50, + "right_line": 52 + }, + { + "left_line": 51, + "right_line": 53 + }, + { + "left_line": 52, + "right_line": 54 + }, + { + "left_line": 53, + "right_line": 55 + }, + { + "left_line": 54, + "right_line": 56 + }, + { + "left_line": 55, + "right_line": 57 + }, + { + "left_line": 56, + "right_line": 58 + }, + { + "left_line": 57, + "right_line": 59 + }, + { + "left_line": 58, + "right_line": 60 + }, + { + "left_line": 59, + "right_line": 61 + }, + { + "left_line": 60, + "right_line": 62 + }, + { + "left_line": 61, + "right_line": 63 + }, + { + "left_line": 62, + "right_line": 64 + }, + { + "left_line": 63, + "right_line": 65 + }, + { + "left_line": 64, + "right_line": 66 + }, + { + "left_line": 65, + "right_line": 67 + }, + { + "left_line": 66, + "right_line": 68 + }, + { + "left_line": 67, + "right_line": 69 + }, + { + "left_line": 68, + "right_line": 70 + }, + { + "left_line": 69, + "right_line": 71 + }, + { + "left_line": 70, + "right_line": 72 + }, + { + "left_line": 71, + "right_line": 73 + }, + { + "left_line": 72, + "right_line": 74 + }, + { + "left_line": 73, + "right_line": 75 + }, + { + "left_line": 74, + "right_line": 76 + }, + { + "left_line": 75, + "right_line": 77 + }, + { + "left_line": 76, + "right_line": 78 + }, + { + "left_line": 77, + "right_line": 79 + }, + { + "left_line": 78, + "right_line": 80 + }, + { + "left_line": 79, + "right_line": 81 + }, + { + "left_line": 80, + "right_line": 82 + }, + { + "left_line": 81, + "right_line": 83 + }, + { + "left_line": 82, + "right_line": 84 + }, + { + "left_line": 83, + "right_line": 85 + }, + { + "left_line": 84, + "right_line": 86 + }, + { + "left_line": 85, + "right_line": 87 + }, + { + "left_line": 86, + "right_line": 88 + }, + { + "left_line": 87, + "right_line": 89 + }, + { + "left_line": 88, + "right_line": 90 + }, + { + "left_line": 89, + "right_line": 91 + }, + { + "left_line": 90, + "right_line": 92 + }, + { + "left_line": 91, + "right_line": 93 + }, + { + "left_line": 92, + "right_line": 94 + }, + { + "left_line": 93, + "right_line": 95 + }, + { + "left_line": 94, + "right_line": 96 + }, + { + "left_line": 95, + "right_line": 97 + }, + { + "left_line": 96, + "right_line": 98 + }, + { + "left_line": 97, + "right_line": 99 + }, + { + "left_line": 98, + "right_line": 100 + }, + { + "left_line": 99, + "right_line": 101 + }, + { + "left_line": 100, + "right_line": 102 + }, + { + "left_line": 101, + "right_line": 103 + }, + { + "left_line": 102, + "right_line": 104 + }, + { + "left_line": 103, + "right_line": 105 + }, + { + "left_line": 104, + "right_line": 106 + }, + { + "left_line": 105, + "right_line": 107 + }, + { + "left_line": 106, + "right_line": 108 + }, + { + "left_line": 107, + "right_line": 109 + }, + { + "left_line": 108, + "right_line": 110 + }, + { + "left_line": 109, + "right_line": 111 + }, + { + "left_line": 110, + "right_line": 112 + }, + { + "left_line": 111, + "right_line": 113 + }, + { + "left_line": 112, + "right_line": 114 + }, + { + "left_line": 113, + "right_line": 115 + }, + { + "left_line": 114, + "right_line": 116 + }, + { + "left_line": 115, + "right_line": 117 + }, + { + "left_line": 116, + "right_line": 118 + }, + { + "left_line": 117, + "right_line": 119 + }, + { + "left_line": 118, + "right_line": 120 + }, + { + "left_line": 119, + "right_line": 121 + }, + { + "left_line": 120, + "right_line": 122 + }, + { + "left_line": 121, + "right_line": 123 + }, + { + "left_line": 122, + "right_line": 124 + }, + { + "left_line": 123, + "right_line": 125 + }, + { + "left_line": 124, + "right_line": 126 + }, + { + "left_line": 125, + "right_line": 127 + }, + { + "left_line": 126, + "right_line": 128 + }, + { + "left_line": 127, + "right_line": 129 + }, + { + "left_line": 128, + "right_line": 130 + }, + { + "left_line": 129, + "right_line": 131 + }, + { + "left_line": 130, + "right_line": 132 + }, + { + "left_line": 131, + "right_line": 133 + }, + { + "left_line": 132, + "right_line": 134 + }, + { + "left_line": 133, + "right_line": 135 + }, + { + "left_line": 134, + "right_line": 136 + }, + { + "left_line": 135, + "right_line": 137 + }, + { + "left_line": 136, + "right_line": 138 + }, + { + "left_line": 137, + "right_line": 139 + }, + { + "left_line": 138, + "right_line": 140 + }, + { + "left_line": 139, + "right_line": 141 + }, + { + "left_line": 140, + "right_line": 142 + }, + { + "left_line": 141, + "right_line": 143 + }, + { + "left_line": 142, + "right_line": 144 + }, + { + "left_line": 143, + "right_line": 145 + }, + { + "left_line": 144, + "right_line": 146 + }, + { + "left_line": 145, + "right_line": 147 + }, + { + "left_line": 146, + "right_line": 148 + }, + { + "left_line": 147, + "right_line": 149 + }, + { + "left_line": 148, + "right_line": 150 + }, + { + "left_line": 149, + "right_line": 151 + }, + { + "left_line": 150, + "right_line": 152 + }, + { + "left_line": 151, + "right_line": 153 + }, + { + "left_line": 152, + "right_line": 154 + }, + { + "left_line": 153, + "right_line": 155 + }, + { + "left_line": 154, + "right_line": 156 + }, + { + "left_line": 155, + "right_line": 157 + }, + { + "left_line": 156, + "right_line": 158 + }, + { + "left_line": 157, + "right_line": 159 + }, + { + "left_line": 158, + "right_line": 160 + }, + { + "left_line": 159, + "right_line": 161 + }, + { + "left_line": 160, + "right_line": 162 + }, + { + "left_line": 161, + "right_line": 163 + }, + { + "left_line": 162, + "right_line": 164 + }, + { + "left_line": 163, + "right_line": 165 + }, + { + "left_line": 164, + "right_line": 166 + }, + { + "left_line": 165, + "right_line": 167 + }, + { + "left_line": 166, + "right_line": 168 + }, + { + "left_line": 167, + "right_line": 169 + }, + { + "left_line": 168, + "right_line": 170 + }, + { + "left_line": 169, + "right_line": 171 + }, + { + "left_line": 170, + "right_line": 172 + }, + { + "left_line": 171, + "right_line": 173 + }, + { + "left_line": 172, + "right_line": 174 + }, + { + "left_line": 173, + "right_line": 175 + }, + { + "left_line": 174, + "right_line": 176 + }, + { + "left_line": 175, + "right_line": 177 + }, + { + "left_line": 176, + "right_line": 178 + }, + { + "left_line": 177, + "right_line": 179 + }, + { + "left_line": 178, + "right_line": 180 + }, + { + "left_line": 179, + "right_line": 181 + }, + { + "left_line": 180, + "right_line": 182 + }, + { + "left_line": 181, + "right_line": 183 + }, + { + "left_line": 182, + "right_line": 184 + }, + { + "left_line": 183, + "right_line": 185 + }, + { + "left_line": 184, + "right_line": 186 + }, + { + "left_line": 185, + "right_line": 187 + }, + { + "left_line": 186, + "right_line": 188 + }, + { + "left_line": 187, + "right_line": 189 + }, + { + "left_line": 188, + "right_line": 190 + }, + { + "left_line": 189, + "right_line": 191 + }, + { + "left_line": 190, + "right_line": 192 + }, + { + "left_line": 191, + "right_line": 193 + }, + { + "left_line": 192, + "right_line": 194 + }, + { + "left_line": 193, + "right_line": 195 + }, + { + "left_line": 194, + "right_line": 196 + }, + { + "left_line": 195, + "right_line": 197 + }, + { + "left_line": 196, + "right_line": 198 + }, + { + "left_line": 197, + "right_line": 199 + }, + { + "left_line": 198, + "right_line": 200 + }, + { + "left_line": 199, + "right_line": 201 + }, + { + "left_line": 200, + "right_line": 202 + }, + { + "left_line": 201, + "right_line": 203 + }, + { + "left_line": 202, + "right_line": 204 + }, + { + "left_line": 203, + "right_line": 205 + }, + { + "left_line": 204, + "right_line": 206 + }, + { + "left_line": 205, + "right_line": 207 + }, + { + "left_line": 206, + "right_line": 208 + }, + { + "left_line": 207, + "right_line": 209 + }, + { + "left_line": 208, + "right_line": 210 + }, + { + "left_line": 209, + "right_line": 211 + }, + { + "left_line": 210, + "right_line": 212 + }, + { + "left_line": 211, + "right_line": 213 + }, + { + "left_line": 212, + "right_line": 214 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/metadata.json b/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/metadata.json new file mode 100644 index 0000000..cfdc215 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "f238dab00870c1e93ed95cb93eac39432e31d54a", + "commit": "6ddd6b63bf91609369b0cfa1248aa9c50ead0921", + "message": "Fix incorrect documentation for Sinatra::IndifferentHash (#1951)\n\nResolves #1948. Looking over the code, I'm not sure that the", + "author": "Jordan Owens", + "date": "2023-10-10T09:15:23-04:00", + "language": "ruby", + "files_changed": 1, + "insertions": 4, + "deletions": 2 +} diff --git a/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/new.rb b/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/new.rb new file mode 100644 index 0000000..f520837 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/new.rb @@ -0,0 +1,214 @@ +# frozen_string_literal: true + +module Sinatra + # A poor man's ActiveSupport::HashWithIndifferentAccess, with all the Rails-y + # stuff removed. + # + # Implements a hash where keys :foo and "foo" are + # considered to be the same. + # + # rgb = Sinatra::IndifferentHash.new + # + # rgb[:black] = '#000000' # symbol assignment + # rgb[:black] # => '#000000' # symbol retrieval + # rgb['black'] # => '#000000' # string retrieval + # + # rgb['white'] = '#FFFFFF' # string assignment + # rgb[:white] # => '#FFFFFF' # symbol retrieval + # rgb['white'] # => '#FFFFFF' # string retrieval + # + # Internally, symbols are mapped to strings when used as keys in the entire + # writing interface (calling e.g. []=, merge). This mapping + # belongs to the public interface. For example, given: + # + # hash = Sinatra::IndifferentHash.new + # hash[:a] = 1 + # + # You are guaranteed that the key is returned as a string: + # + # hash.keys # => ["a"] + # + # Technically other types of keys are accepted: + # + # hash = Sinatra::IndifferentHash + # hash[:a] = 1 + # hash[0] = 0 + # hash # => { "a"=>1, 0=>0 } + # + # But this class is intended for use cases where strings or symbols are the + # expected keys and it is convenient to understand both as the same. For + # example the +params+ hash in Sinatra. + class IndifferentHash < Hash + def self.[](*args) + new.merge!(Hash[*args]) + end + + def initialize(*args) + args.map!(&method(:convert_value)) + + super(*args) + end + + def default(*args) + args.map!(&method(:convert_key)) + + super(*args) + end + + def default=(value) + super(convert_value(value)) + end + + def assoc(key) + super(convert_key(key)) + end + + def rassoc(value) + super(convert_value(value)) + end + + def fetch(key, *args) + args.map!(&method(:convert_value)) + + super(convert_key(key), *args) + end + + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), convert_value(value)) + end + + alias store []= + + def key(value) + super(convert_value(value)) + end + + def key?(key) + super(convert_key(key)) + end + + alias has_key? key? + alias include? key? + alias member? key? + + def value?(value) + super(convert_value(value)) + end + + alias has_value? value? + + def delete(key) + super(convert_key(key)) + end + + # Added in Ruby 2.3 + def dig(key, *other_keys) + super(convert_key(key), *other_keys) + end + + def fetch_values(*keys) + keys.map!(&method(:convert_key)) + + super(*keys) + end + + def slice(*keys) + keys.map!(&method(:convert_key)) + + self.class[super(*keys)] + end + + def values_at(*keys) + keys.map!(&method(:convert_key)) + + super(*keys) + end + + def merge!(*other_hashes) + other_hashes.each do |other_hash| + if other_hash.is_a?(self.class) + super(other_hash) + else + other_hash.each_pair do |key, value| + key = convert_key(key) + value = yield(key, self[key], value) if block_given? && key?(key) + self[key] = convert_value(value) + end + end + end + + self + end + + alias update merge! + + def merge(*other_hashes, &block) + dup.merge!(*other_hashes, &block) + end + + def replace(other_hash) + super(other_hash.is_a?(self.class) ? other_hash : self.class[other_hash]) + end + + def transform_values(&block) + dup.transform_values!(&block) + end + + def transform_values! + super + super(&method(:convert_value)) + end + + def transform_keys(&block) + dup.transform_keys!(&block) + end + + def transform_keys! + super + super(&method(:convert_key)) + end + + def select(*args, &block) + return to_enum(:select) unless block_given? + + dup.tap { |hash| hash.select!(*args, &block) } + end + + def reject(*args, &block) + return to_enum(:reject) unless block_given? + + dup.tap { |hash| hash.reject!(*args, &block) } + end + + def compact + dup.tap(&:compact!) + end + + def except(*keys) + keys.map!(&method(:convert_key)) + + super(*keys) + end if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0") + + private + + def convert_key(key) + key.is_a?(Symbol) ? key.to_s : key + end + + def convert_value(value) + case value + when Hash + value.is_a?(self.class) ? value : self.class[value] + when Array + value.map(&method(:convert_value)) + else + value + end + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/old.rb b/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/old.rb new file mode 100644 index 0000000..f6ae52c --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_incorrect_documentation_fo_11/old.rb @@ -0,0 +1,212 @@ +# frozen_string_literal: true + +module Sinatra + # A poor man's ActiveSupport::HashWithIndifferentAccess, with all the Rails-y + # stuff removed. + # + # Implements a hash where keys :foo and "foo" are + # considered to be the same. + # + # rgb = Sinatra::IndifferentHash.new + # + # rgb[:black] = '#000000' # symbol assignment + # rgb[:black] # => '#000000' # symbol retrieval + # rgb['black'] # => '#000000' # string retrieval + # + # rgb['white'] = '#FFFFFF' # string assignment + # rgb[:white] # => '#FFFFFF' # symbol retrieval + # rgb['white'] # => '#FFFFFF' # string retrieval + # + # Internally, symbols are mapped to strings when used as keys in the entire + # writing interface (calling e.g. []=, merge). This mapping + # belongs to the public interface. For example, given: + # + # hash = Sinatra::IndifferentHash.new(:a=>1) + # + # You are guaranteed that the key is returned as a string: + # + # hash.keys # => ["a"] + # + # Technically other types of keys are accepted: + # + # hash = Sinatra::IndifferentHash.new(:a=>1) + # hash[0] = 0 + # hash # => { "a"=>1, 0=>0 } + # + # But this class is intended for use cases where strings or symbols are the + # expected keys and it is convenient to understand both as the same. For + # example the +params+ hash in Sinatra. + class IndifferentHash < Hash + def self.[](*args) + new.merge!(Hash[*args]) + end + + def initialize(*args) + args.map!(&method(:convert_value)) + + super(*args) + end + + def default(*args) + args.map!(&method(:convert_key)) + + super(*args) + end + + def default=(value) + super(convert_value(value)) + end + + def assoc(key) + super(convert_key(key)) + end + + def rassoc(value) + super(convert_value(value)) + end + + def fetch(key, *args) + args.map!(&method(:convert_value)) + + super(convert_key(key), *args) + end + + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), convert_value(value)) + end + + alias store []= + + def key(value) + super(convert_value(value)) + end + + def key?(key) + super(convert_key(key)) + end + + alias has_key? key? + alias include? key? + alias member? key? + + def value?(value) + super(convert_value(value)) + end + + alias has_value? value? + + def delete(key) + super(convert_key(key)) + end + + # Added in Ruby 2.3 + def dig(key, *other_keys) + super(convert_key(key), *other_keys) + end + + def fetch_values(*keys) + keys.map!(&method(:convert_key)) + + super(*keys) + end + + def slice(*keys) + keys.map!(&method(:convert_key)) + + self.class[super(*keys)] + end + + def values_at(*keys) + keys.map!(&method(:convert_key)) + + super(*keys) + end + + def merge!(*other_hashes) + other_hashes.each do |other_hash| + if other_hash.is_a?(self.class) + super(other_hash) + else + other_hash.each_pair do |key, value| + key = convert_key(key) + value = yield(key, self[key], value) if block_given? && key?(key) + self[key] = convert_value(value) + end + end + end + + self + end + + alias update merge! + + def merge(*other_hashes, &block) + dup.merge!(*other_hashes, &block) + end + + def replace(other_hash) + super(other_hash.is_a?(self.class) ? other_hash : self.class[other_hash]) + end + + def transform_values(&block) + dup.transform_values!(&block) + end + + def transform_values! + super + super(&method(:convert_value)) + end + + def transform_keys(&block) + dup.transform_keys!(&block) + end + + def transform_keys! + super + super(&method(:convert_key)) + end + + def select(*args, &block) + return to_enum(:select) unless block_given? + + dup.tap { |hash| hash.select!(*args, &block) } + end + + def reject(*args, &block) + return to_enum(:reject) unless block_given? + + dup.tap { |hash| hash.reject!(*args, &block) } + end + + def compact + dup.tap(&:compact!) + end + + def except(*keys) + keys.map!(&method(:convert_key)) + + super(*keys) + end if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0") + + private + + def convert_key(key) + key.is_a?(Symbol) ? key.to_s : key + end + + def convert_value(value) + case value + when Hash + value.is_a?(self.class) ? value : self.class[value] + when Array + value.map(&method(:convert_value)) + else + value + end + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/expected.json b/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/expected.json new file mode 100644 index 0000000..8a6529b --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/expected.json @@ -0,0 +1,317 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 3, + 2, + 11, + 6 + ], + "type": "comment", + "label": "# BUNDLE_LOCKFILE is exported by Bundler 4; without clearing it the child would", + "start_byte": 1302, + "end_byte": 1381 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 3, + 2, + 11 + ], + "type": "body_statement", + "start_byte": 865, + "end_byte": 2023 + }, + "position": 6 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 3, + 2, + 11, + 7 + ], + "type": "comment", + "label": "# write the alternate gemfile's lock to the parent project's Gemfile.lock path", + "start_byte": 1386, + "end_byte": 1464 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 3, + 2, + 11 + ], + "type": "body_statement", + "start_byte": 865, + "end_byte": 2023 + }, + "position": 7 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 3, + 2, + 11, + 8, + 2, + 1 + ], + "type": "pair", + "start_byte": 1507, + "end_byte": 1531 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 3, + 2, + 11, + 8, + 2 + ], + "type": "hash", + "start_byte": 1475, + "end_byte": 1533 + }, + "position": 1, + "subtree": true + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": -1, + "right_line": 23 + }, + { + "left_line": -1, + "right_line": 24 + }, + { + "left_line": 23, + "right_line": 25 + }, + { + "left_line": 24, + "right_line": 26 + }, + { + "left_line": 25, + "right_line": 27 + }, + { + "left_line": 26, + "right_line": 28 + }, + { + "left_line": 27, + "right_line": 29 + }, + { + "left_line": 28, + "right_line": 30 + }, + { + "left_line": 29, + "right_line": 31 + }, + { + "left_line": 30, + "right_line": 32 + }, + { + "left_line": 31, + "right_line": 33 + }, + { + "left_line": 32, + "right_line": 34 + }, + { + "left_line": 33, + "right_line": 35 + }, + { + "left_line": 34, + "right_line": 36 + }, + { + "left_line": 35, + "right_line": 37 + }, + { + "left_line": 36, + "right_line": 38 + }, + { + "left_line": 37, + "right_line": 39 + }, + { + "left_line": 38, + "right_line": 40 + }, + { + "left_line": 39, + "right_line": 41 + }, + { + "left_line": 40, + "right_line": 42 + }, + { + "left_line": 41, + "right_line": 43 + }, + { + "left_line": 42, + "right_line": 44 + }, + { + "left_line": 43, + "right_line": 45 + }, + { + "left_line": 44, + "right_line": 46 + }, + { + "left_line": 45, + "right_line": 47 + }, + { + "left_line": 46, + "right_line": 48 + }, + { + "left_line": 47, + "right_line": 49 + }, + { + "left_line": 48, + "right_line": 50 + }, + { + "left_line": 49, + "right_line": 51 + }, + { + "left_line": 50, + "right_line": 52 + }, + { + "left_line": 51, + "right_line": 53 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/metadata.json b/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/metadata.json new file mode 100644 index 0000000..5a4b958 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "f891dd2b6f4911e356600efe6c3b82af97d262c6", + "commit": "a0ed7f4cd5df24fce0542b2934b4f9409fed2ce1", + "message": "Fix integration test corrupting Gemfile.lock under Bundler 4\n\n`test_app_start_without_rackup` spawns a child Ruby with a custom", + "author": "Patrik Ragnarsson", + "date": "2026-04-28T21:59:39Z", + "language": "ruby", + "files_changed": 1, + "insertions": 3, + "deletions": 1 +} diff --git a/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/new.rb b/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/new.rb new file mode 100644 index 0000000..44da05c --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/new.rb @@ -0,0 +1,53 @@ +require_relative "integration_start_helper" + +class IntegrationStartTest < Minitest::Test + include IntegrationStartHelper + + # what we test here: that the correct help text is printed when the required gems aren't installed + def test_app_start_without_rackup + # Why we skip head versions: The Gemfile used here would have to support + # the ENVs and we would need to bundle before starting the app + # + # Example from locally playing with this: + # + # root@df8b1e7cb106:/app# rack_session=head BUNDLE_GEMFILE=./test/integration/gemfile_without_rackup.rb ruby ./test/integration/simple_app.rb -p 0 -s puma + # The git source https://github.com/rack/rack-session.git is not yet checked out. Please run `bundle install` before trying to start your application + # + # Using bundler/inline is an idea, but it would add to the startup time + skip "So much work to run with rack head branch" if ENV['rack'] == 'head' + skip "So much work to run with rack-session head branch" if ENV['rack_session'] == 'head' + + app_file = File.join(__dir__, "integration", "simple_app.rb") + gem_file = File.join(__dir__, "integration", "gemfile_without_rackup.rb") + lock_file = File.join(__dir__, "integration", "gemfile_without_rackup.rb.lock") + command = command_for(app_file) + # BUNDLE_LOCKFILE is exported by Bundler 4; without clearing it the child would + # write the alternate gemfile's lock to the parent project's Gemfile.lock path + env = { "BUNDLE_GEMFILE" => gem_file, "BUNDLE_LOCKFILE" => nil } + + with_process(command: command, env: env) do |process, read_io| + assert wait_for_output(read_io, /Sinatra could not start, the required gems weren't found/) + end + # this ensure block runs even if the test is skipped + ensure + # when the command is run, at least with bundler 2.6.9, test/integration/gemfile_without_rackup.rb.lock is created + # we need to clean it up to avoid problems on consecutive runs + File.delete(lock_file) if lock_file && File.exist?(lock_file) + end + + def test_classic_app_start + app_file = File.join(__dir__, "integration", "simple_app.rb") + command = command_for(app_file) + with_process(command: command) do |process, read_io| + assert wait_for_output(read_io, /Sinatra \(v.+\) has taken the stage/) + end + end + + def test_classic_app_with_zeitwerk + app_file = File.join(__dir__, "integration", "zeitwerk_app.rb") + command = command_for(app_file) + with_process(command: command) do |process, read_io| + assert wait_for_output(read_io, /Sinatra \(v.+\) has taken the stage/) + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/old.rb b/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/old.rb new file mode 100644 index 0000000..1e29d95 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_integration_test_corruptin_5/old.rb @@ -0,0 +1,51 @@ +require_relative "integration_start_helper" + +class IntegrationStartTest < Minitest::Test + include IntegrationStartHelper + + # what we test here: that the correct help text is printed when the required gems aren't installed + def test_app_start_without_rackup + # Why we skip head versions: The Gemfile used here would have to support + # the ENVs and we would need to bundle before starting the app + # + # Example from locally playing with this: + # + # root@df8b1e7cb106:/app# rack_session=head BUNDLE_GEMFILE=./test/integration/gemfile_without_rackup.rb ruby ./test/integration/simple_app.rb -p 0 -s puma + # The git source https://github.com/rack/rack-session.git is not yet checked out. Please run `bundle install` before trying to start your application + # + # Using bundler/inline is an idea, but it would add to the startup time + skip "So much work to run with rack head branch" if ENV['rack'] == 'head' + skip "So much work to run with rack-session head branch" if ENV['rack_session'] == 'head' + + app_file = File.join(__dir__, "integration", "simple_app.rb") + gem_file = File.join(__dir__, "integration", "gemfile_without_rackup.rb") + lock_file = File.join(__dir__, "integration", "gemfile_without_rackup.rb.lock") + command = command_for(app_file) + env = { "BUNDLE_GEMFILE" => gem_file } + + with_process(command: command, env: env) do |process, read_io| + assert wait_for_output(read_io, /Sinatra could not start, the required gems weren't found/) + end + # this ensure block runs even if the test is skipped + ensure + # when the command is run, at least with bundler 2.6.9, test/integration/gemfile_without_rackup.rb.lock is created + # we need to clean it up to avoid problems on consecutive runs + File.delete(lock_file) if lock_file && File.exist?(lock_file) + end + + def test_classic_app_start + app_file = File.join(__dir__, "integration", "simple_app.rb") + command = command_for(app_file) + with_process(command: command) do |process, read_io| + assert wait_for_output(read_io, /Sinatra \(v.+\) has taken the stage/) + end + end + + def test_classic_app_with_zeitwerk + app_file = File.join(__dir__, "integration", "zeitwerk_app.rb") + command = command_for(app_file) + with_process(command: command) do |process, read_io| + assert wait_for_output(read_io, /Sinatra \(v.+\) has taken the stage/) + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/expected.json b/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/expected.json new file mode 100644 index 0000000..8b2bfeb --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/expected.json @@ -0,0 +1,1179 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 4 + ], + "type": "comment", + "label": "# commonmarker 2.9.0 changed how heading anchors are rendered: the anchor now comes", + "start_byte": 461, + "end_byte": 544 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1 + ], + "type": "body_statement", + "start_byte": 57, + "end_byte": 3109 + }, + "position": 4 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 5 + ], + "type": "comment", + "label": "# after the heading text and uses aria-label/data-heading-content instead of aria-hidden", + "start_byte": 547, + "end_byte": 635 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1 + ], + "type": "body_statement", + "start_byte": 57, + "end_byte": 3109 + }, + "position": 5 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 6 + ], + "type": "method", + "start_byte": 638, + "end_byte": 781 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1 + ], + "type": "body_statement", + "start_byte": 57, + "end_byte": 3109 + }, + "position": 6, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2, + 1 + ], + "type": "call", + "start_byte": 1039, + "end_byte": 1067 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1036, + "end_byte": 1464 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2, + 2 + ], + "type": "then", + "start_byte": 1067, + "end_byte": 1253 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1036, + "end_byte": 1464 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2, + 3 + ], + "type": "elsif", + "start_byte": 1258, + "end_byte": 1456 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1036, + "end_byte": 1464 + }, + "position": 3 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2, + 1 + ], + "type": "call", + "start_byte": 1579, + "end_byte": 1607 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1576, + "end_byte": 2185 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2, + 2 + ], + "type": "then", + "start_byte": 1607, + "end_byte": 1892 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1576, + "end_byte": 2185 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2, + 3 + ], + "type": "elsif", + "start_byte": 1897, + "end_byte": 2177 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1576, + "end_byte": 2185 + }, + "position": 3 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 5, + 2, + 1, + 2, + 1 + ], + "type": "call", + "start_byte": 715, + "end_byte": 741 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2, + 3 + ], + "type": "elsif", + "start_byte": 1258, + "end_byte": 1456 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 5, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 712, + "end_byte": 915 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 1264, + "dest_end_byte": 1290, + "group_id": "group-1" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 5, + 2, + 1, + 2, + 2 + ], + "type": "then", + "start_byte": 741, + "end_byte": 856 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2, + 3 + ], + "type": "elsif", + "start_byte": 1258, + "end_byte": 1456 + }, + "position": 2, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 5, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 712, + "end_byte": 915 + }, + "old_position": 2, + "subtree": true, + "dest_start_byte": 1290, + "dest_end_byte": 1405, + "group_id": "group-1" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 5, + 2, + 1, + 2, + 3 + ], + "type": "else", + "start_byte": 861, + "end_byte": 907 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 8, + 2, + 1, + 2, + 3 + ], + "type": "elsif", + "start_byte": 1258, + "end_byte": 1456 + }, + "position": 3, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 5, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 712, + "end_byte": 915 + }, + "old_position": 3, + "subtree": true, + "dest_start_byte": 1410, + "dest_end_byte": 1456 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 6, + 2, + 1, + 2, + 1 + ], + "type": "call", + "start_byte": 1030, + "end_byte": 1056 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2, + 3 + ], + "type": "elsif", + "start_byte": 1897, + "end_byte": 2177 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 6, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1027, + "end_byte": 1312 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 1903, + "dest_end_byte": 1929, + "group_id": "group-2" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 6, + 2, + 1, + 2, + 2 + ], + "type": "then", + "start_byte": 1056, + "end_byte": 1240 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2, + 3 + ], + "type": "elsif", + "start_byte": 1897, + "end_byte": 2177 + }, + "position": 2, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 6, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1027, + "end_byte": 1312 + }, + "old_position": 2, + "subtree": true, + "dest_start_byte": 1929, + "dest_end_byte": 2113, + "group_id": "group-2" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 6, + 2, + 1, + 2, + 3 + ], + "type": "else", + "start_byte": 1245, + "end_byte": 1304 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 1, + 1, + 9, + 2, + 1, + 2, + 3 + ], + "type": "elsif", + "start_byte": 1897, + "end_byte": 2177 + }, + "position": 3, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 1, + 1, + 6, + 2, + 1, + 2 + ], + "type": "if", + "start_byte": 1027, + "end_byte": 1312 + }, + "old_position": 3, + "subtree": true, + "dest_start_byte": 2118, + "dest_end_byte": 2177 + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": -1, + "right_line": 21 + }, + { + "left_line": -1, + "right_line": 22 + }, + { + "left_line": -1, + "right_line": 23 + }, + { + "left_line": -1, + "right_line": 24 + }, + { + "left_line": -1, + "right_line": 25 + }, + { + "left_line": -1, + "right_line": 26 + }, + { + "left_line": 21, + "right_line": 27 + }, + { + "left_line": 22, + "right_line": 28 + }, + { + "left_line": 23, + "right_line": 29 + }, + { + "left_line": 24, + "right_line": 30 + }, + { + "left_line": 25, + "right_line": 31 + }, + { + "left_line": 26, + "right_line": 32 + }, + { + "left_line": 27, + "right_line": 33 + }, + { + "left_line": 28, + "right_line": 34 + }, + { + "left_line": 29, + "right_line": 35 + }, + { + "left_line": 30, + "right_line": -1 + }, + { + "left_line": 31, + "right_line": -1 + }, + { + "left_line": 32, + "right_line": -1 + }, + { + "left_line": 33, + "right_line": -1 + }, + { + "left_line": -1, + "right_line": 36 + }, + { + "left_line": -1, + "right_line": 37 + }, + { + "left_line": -1, + "right_line": 38 + }, + { + "left_line": -1, + "right_line": 39 + }, + { + "left_line": -1, + "right_line": 40 + }, + { + "left_line": -1, + "right_line": 41 + }, + { + "left_line": -1, + "right_line": 42 + }, + { + "left_line": 34, + "right_line": 43 + }, + { + "left_line": 35, + "right_line": 44 + }, + { + "left_line": 36, + "right_line": 45 + }, + { + "left_line": 37, + "right_line": 46 + }, + { + "left_line": 38, + "right_line": 47 + }, + { + "left_line": 39, + "right_line": 48 + }, + { + "left_line": 40, + "right_line": -1 + }, + { + "left_line": 41, + "right_line": -1 + }, + { + "left_line": 42, + "right_line": -1 + }, + { + "left_line": 43, + "right_line": -1 + }, + { + "left_line": 44, + "right_line": -1 + }, + { + "left_line": -1, + "right_line": 49 + }, + { + "left_line": -1, + "right_line": 50 + }, + { + "left_line": -1, + "right_line": 51 + }, + { + "left_line": -1, + "right_line": 52 + }, + { + "left_line": -1, + "right_line": 53 + }, + { + "left_line": -1, + "right_line": 54 + }, + { + "left_line": -1, + "right_line": 55 + }, + { + "left_line": -1, + "right_line": 56 + }, + { + "left_line": -1, + "right_line": 57 + }, + { + "left_line": 45, + "right_line": 58 + }, + { + "left_line": 46, + "right_line": 59 + }, + { + "left_line": 47, + "right_line": 60 + }, + { + "left_line": 48, + "right_line": 61 + }, + { + "left_line": 49, + "right_line": 62 + }, + { + "left_line": 50, + "right_line": 63 + }, + { + "left_line": 51, + "right_line": 64 + }, + { + "left_line": 52, + "right_line": 65 + }, + { + "left_line": 53, + "right_line": 66 + }, + { + "left_line": 54, + "right_line": 67 + }, + { + "left_line": 55, + "right_line": 68 + }, + { + "left_line": 56, + "right_line": 69 + }, + { + "left_line": 57, + "right_line": 70 + }, + { + "left_line": 58, + "right_line": 71 + }, + { + "left_line": 59, + "right_line": 72 + }, + { + "left_line": 60, + "right_line": 73 + }, + { + "left_line": 61, + "right_line": 74 + }, + { + "left_line": 62, + "right_line": 75 + }, + { + "left_line": 63, + "right_line": 76 + }, + { + "left_line": 64, + "right_line": 77 + }, + { + "left_line": 65, + "right_line": 78 + }, + { + "left_line": 66, + "right_line": 79 + }, + { + "left_line": 67, + "right_line": 80 + }, + { + "left_line": 68, + "right_line": 81 + }, + { + "left_line": 69, + "right_line": 82 + }, + { + "left_line": 70, + "right_line": 83 + }, + { + "left_line": 71, + "right_line": 84 + }, + { + "left_line": 72, + "right_line": 85 + }, + { + "left_line": 73, + "right_line": 86 + }, + { + "left_line": 74, + "right_line": 87 + }, + { + "left_line": 75, + "right_line": 88 + }, + { + "left_line": 76, + "right_line": 89 + }, + { + "left_line": 77, + "right_line": 90 + }, + { + "left_line": 78, + "right_line": 91 + }, + { + "left_line": 79, + "right_line": 92 + }, + { + "left_line": 80, + "right_line": 93 + }, + { + "left_line": 81, + "right_line": 94 + }, + { + "left_line": 82, + "right_line": 95 + }, + { + "left_line": 83, + "right_line": 96 + }, + { + "left_line": 84, + "right_line": 97 + }, + { + "left_line": 85, + "right_line": 98 + }, + { + "left_line": 86, + "right_line": 99 + }, + { + "left_line": 87, + "right_line": 100 + }, + { + "left_line": 88, + "right_line": 101 + }, + { + "left_line": 89, + "right_line": 102 + }, + { + "left_line": 90, + "right_line": 103 + }, + { + "left_line": 91, + "right_line": 104 + }, + { + "left_line": 92, + "right_line": 105 + }, + { + "left_line": 93, + "right_line": 106 + }, + { + "left_line": 94, + "right_line": 107 + }, + { + "left_line": 95, + "right_line": 108 + }, + { + "left_line": 96, + "right_line": 109 + }, + { + "left_line": 97, + "right_line": 110 + }, + { + "left_line": 98, + "right_line": 111 + }, + { + "left_line": 99, + "right_line": 112 + }, + { + "left_line": 100, + "right_line": 113 + }, + { + "left_line": 101, + "right_line": 114 + }, + { + "left_line": 102, + "right_line": 115 + }, + { + "left_line": 103, + "right_line": 116 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/metadata.json b/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/metadata.json new file mode 100644 index 0000000..ad3d493 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "0c88089be7668326ec5ed52671732f8565a16353", + "commit": "cb22afd7902b566b6eaba6c4ea89739494a65d12", + "message": "Fix markdown tests for commonmarker 2.9.0's heading anchor markup (#2182)\n\nFixes #2181.", + "author": "hexonal", + "date": "2026-07-20T05:59:21-04:00", + "language": "ruby", + "files_changed": 1, + "insertions": 15, + "deletions": 2 +} diff --git a/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/new.rb b/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/new.rb new file mode 100644 index 0000000..55d3284 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/new.rb @@ -0,0 +1,116 @@ +require_relative 'test_helper' + +MarkdownTest = proc do + def markdown_app(&block) + mock_app do + set :views, __dir__ + '/views' + get('/', &block) + end + get '/' + end + + def setup + Tilt.prefer engine, 'markdown', 'mkd', 'md' + super + end + + # commonmarker is not installed on all platforms (e.g. jruby) + def commonmarker_v1_or_higher? + defined?(CommonMarkerTest) && self.class == CommonMarkerTest && defined?(::Commonmarker) + end + + # commonmarker 2.9.0 changed how heading anchors are rendered: the anchor now comes + # after the heading text and uses aria-label/data-heading-content instead of aria-hidden + def commonmarker_v2_9_or_higher? + commonmarker_v1_or_higher? && Gem::Version.new(::Commonmarker::VERSION) >= Gem::Version.new('2.9.0') + end + + it 'uses the correct engine' do + assert_equal engine, Tilt[:md] + assert_equal engine, Tilt[:mkd] + assert_equal engine, Tilt[:markdown] + end + + it 'renders inline markdown strings' do + markdown_app { markdown '# Hiya' } + assert ok? + if commonmarker_v2_9_or_higher? + assert_equal "

Hiya

\n", body + elsif commonmarker_v1_or_higher? + assert_equal "

Hiya

\n", body + else + assert_like "

Hiya

\n", body + end + end + + it 'renders .markdown files in views path' do + markdown_app { markdown :hello } + assert ok? + if commonmarker_v2_9_or_higher? + assert_equal "

Hello From Markdown

\n", body + elsif commonmarker_v1_or_higher? + assert_equal "

Hello From Markdown

\n", body + else + assert_like "

Hello From Markdown

", body + end + end + + it "raises error if template not found" do + mock_app { get('/') { markdown :no_such_template } } + assert_raises(Errno::ENOENT) { get('/') } + end + + it "renders with inline layouts" do + mock_app do + layout { 'THIS. IS. #{yield.upcase}!' } + get('/') { markdown 'Sparta', :layout_engine => :str } + end + get '/' + assert ok? + assert_like 'THIS. IS.

SPARTA

!', body + end + + it "renders with file layouts" do + markdown_app { + markdown 'Hello World', :layout => :layout2, :layout_engine => :erb + } + assert ok? + assert_body "ERB Layout!\n

Hello World

" + end + + it "can be used in a nested fashion for partials and whatnot" do + mock_app do + template(:inner) { "hi" } + template(:outer) { "<%= markdown :inner %>" } + get('/') { erb :outer } + end + + get '/' + assert ok? + assert_like '

hi

', body + end +end + +[ + "Tilt::PandocTemplate", + "Tilt::CommonMarkerTemplate", + "Tilt::KramdownTemplate", + "Tilt::RedcarpetTemplate", + "Tilt::RDiscountTemplate" +].each do |template_name| + begin + template = Object.const_get(template_name) + + klass = Class.new(Minitest::Test) { define_method(:engine) { template } } + klass.class_eval(&MarkdownTest) + + name = template_name.split('::').last.sub(/Template$/, 'Test') + Object.const_set name, klass + rescue LoadError, NameError + warn "#{$!}: skipping markdown tests with #{template_name}" + end +end diff --git a/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/old.rb b/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/old.rb new file mode 100644 index 0000000..04418cb --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_markdown_tests_for_commonm_3/old.rb @@ -0,0 +1,103 @@ +require_relative 'test_helper' + +MarkdownTest = proc do + def markdown_app(&block) + mock_app do + set :views, __dir__ + '/views' + get('/', &block) + end + get '/' + end + + def setup + Tilt.prefer engine, 'markdown', 'mkd', 'md' + super + end + + # commonmarker is not installed on all platforms (e.g. jruby) + def commonmarker_v1_or_higher? + defined?(CommonMarkerTest) && self.class == CommonMarkerTest && defined?(::Commonmarker) + end + + it 'uses the correct engine' do + assert_equal engine, Tilt[:md] + assert_equal engine, Tilt[:mkd] + assert_equal engine, Tilt[:markdown] + end + + it 'renders inline markdown strings' do + markdown_app { markdown '# Hiya' } + assert ok? + if commonmarker_v1_or_higher? + assert_equal "

Hiya

\n", body + else + assert_like "

Hiya

\n", body + end + end + + it 'renders .markdown files in views path' do + markdown_app { markdown :hello } + assert ok? + if commonmarker_v1_or_higher? + assert_equal "

Hello From Markdown

\n", body + else + assert_like "

Hello From Markdown

", body + end + end + + it "raises error if template not found" do + mock_app { get('/') { markdown :no_such_template } } + assert_raises(Errno::ENOENT) { get('/') } + end + + it "renders with inline layouts" do + mock_app do + layout { 'THIS. IS. #{yield.upcase}!' } + get('/') { markdown 'Sparta', :layout_engine => :str } + end + get '/' + assert ok? + assert_like 'THIS. IS.

SPARTA

!', body + end + + it "renders with file layouts" do + markdown_app { + markdown 'Hello World', :layout => :layout2, :layout_engine => :erb + } + assert ok? + assert_body "ERB Layout!\n

Hello World

" + end + + it "can be used in a nested fashion for partials and whatnot" do + mock_app do + template(:inner) { "hi" } + template(:outer) { "<%= markdown :inner %>" } + get('/') { erb :outer } + end + + get '/' + assert ok? + assert_like '

hi

', body + end +end + +[ + "Tilt::PandocTemplate", + "Tilt::CommonMarkerTemplate", + "Tilt::KramdownTemplate", + "Tilt::RedcarpetTemplate", + "Tilt::RDiscountTemplate" +].each do |template_name| + begin + template = Object.const_get(template_name) + + klass = Class.new(Minitest::Test) { define_method(:engine) { template } } + klass.class_eval(&MarkdownTest) + + name = template_name.split('::').last.sub(/Template$/, 'Test') + Object.const_set name, klass + rescue LoadError, NameError + warn "#{$!}: skipping markdown tests with #{template_name}" + end +end diff --git a/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/expected.json b/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/expected.json new file mode 100644 index 0000000..5846040 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/expected.json @@ -0,0 +1,10440 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 33 + ], + "type": "identifier", + "label": "private", + "start_byte": 28740, + "end_byte": 28747 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2 + ], + "type": "body_statement", + "start_byte": 21142, + "end_byte": 28994 + }, + "position": 33 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34 + ], + "type": "method", + "start_byte": 28753, + "end_byte": 28994 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2 + ], + "type": "body_statement", + "start_byte": 21142, + "end_byte": 28994 + }, + "position": 34 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 0 + ], + "type": "assignment", + "start_byte": 27604, + "end_byte": 27627 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3 + ], + "type": "body_statement", + "start_byte": 27497, + "end_byte": 28726 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1 + ], + "type": "body_statement", + "start_byte": 27604, + "end_byte": 28787 + }, + "old_position": 0, + "subtree": true, + "dest_start_byte": 27543, + "dest_end_byte": 27566 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 1 + ], + "type": "if_modifier", + "start_byte": 27636, + "end_byte": 27697 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3 + ], + "type": "body_statement", + "start_byte": 27497, + "end_byte": 28726 + }, + "position": 2, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1 + ], + "type": "body_statement", + "start_byte": 27604, + "end_byte": 28787 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 27573, + "dest_end_byte": 27634 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "case", + "label": "case", + "start_byte": 27707, + "end_byte": 27711 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3 + ], + "type": "case", + "start_byte": 27642, + "end_byte": 28726 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2 + ], + "type": "case", + "start_byte": 27707, + "end_byte": 28787 + }, + "old_position": 0, + "dest_start_byte": 27642, + "dest_end_byte": 27646, + "group_id": "group-1" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 1 + ], + "type": "identifier", + "label": "data", + "start_byte": 27712, + "end_byte": 27716 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3 + ], + "type": "case", + "start_byte": 27642, + "end_byte": 28726 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2 + ], + "type": "case", + "start_byte": 27707, + "end_byte": 28787 + }, + "old_position": 1, + "dest_start_byte": 27647, + "dest_end_byte": 27651, + "group_id": "group-1" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 2, + 0 + ], + "type": "when", + "label": "when", + "start_byte": 27725, + "end_byte": 27729 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2 + ], + "type": "when", + "start_byte": 27658, + "end_byte": 28400 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 2 + ], + "type": "when", + "start_byte": 27725, + "end_byte": 28394 + }, + "old_position": 0, + "dest_start_byte": 27658, + "dest_end_byte": 27662, + "group_id": "group-2" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 2, + 1 + ], + "type": "pattern", + "start_byte": 27730, + "end_byte": 27736 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2 + ], + "type": "when", + "start_byte": 27658, + "end_byte": 28400 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 2 + ], + "type": "when", + "start_byte": 27725, + "end_byte": 28394 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 27663, + "dest_end_byte": 27669, + "group_id": "group-2" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 1 + ], + "type": "pattern", + "start_byte": 28408, + "end_byte": 28412 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 3 + ], + "type": "when", + "start_byte": 28407, + "end_byte": 28473 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3 + ], + "type": "when", + "start_byte": 28403, + "end_byte": 28680 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 28412, + "dest_end_byte": 28416, + "group_id": "group-2" + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 2, + 2, + 1 + ], + "type": "identifier", + "label": "new", + "start_byte": 28464, + "end_byte": 28467 + } + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 4 + ], + "type": "else", + "start_byte": 28689, + "end_byte": 28775 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3 + ], + "type": "case", + "start_byte": 27642, + "end_byte": 28726 + }, + "position": 5, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2 + ], + "type": "case", + "start_byte": 27707, + "end_byte": 28787 + }, + "old_position": 4, + "subtree": true, + "dest_start_byte": 28632, + "dest_end_byte": 28716, + "group_id": "group-3" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 5 + ], + "type": "end", + "label": "end", + "start_byte": 28784, + "end_byte": 28787 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3 + ], + "type": "case", + "start_byte": 27642, + "end_byte": 28726 + }, + "position": 6, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2 + ], + "type": "case", + "start_byte": 27707, + "end_byte": 28787 + }, + "old_position": 5, + "dest_start_byte": 28723, + "dest_end_byte": 28726, + "group_id": "group-3" + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34, + 0 + ], + "type": "def", + "label": "def", + "start_byte": 28753, + "end_byte": 28756 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34 + ], + "type": "method", + "start_byte": 28753, + "end_byte": 28994 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34, + 1 + ], + "type": "identifier", + "label": "compile_block_template", + "start_byte": 28757, + "end_byte": 28779 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34 + ], + "type": "method", + "start_byte": 28753, + "end_byte": 28994 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34, + 2 + ], + "type": "method_parameters", + "start_byte": 28779, + "end_byte": 28805 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34 + ], + "type": "method", + "start_byte": 28753, + "end_byte": 28994 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34, + 4 + ], + "type": "end", + "label": "end", + "start_byte": 28991, + "end_byte": 28994 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34 + ], + "type": "method", + "start_byte": 28753, + "end_byte": 28994 + }, + "position": 4 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 0 + ], + "type": "assignment", + "start_byte": 27497, + "end_byte": 27536 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3 + ], + "type": "body_statement", + "start_byte": 27497, + "end_byte": 28726 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3 + ], + "type": "body_statement", + "start_byte": 27497, + "end_byte": 28797 + }, + "old_position": 0, + "subtree": true, + "dest_start_byte": 27497, + "dest_end_byte": 27536 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 1 + ], + "type": "assignment", + "start_byte": 28494, + "end_byte": 28534 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34, + 3 + ], + "type": "body_statement", + "start_byte": 28812, + "end_byte": 28986 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3 + ], + "type": "then", + "start_byte": 28420, + "end_byte": 28680 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 28812, + "dest_end_byte": 28852, + "group_id": "group-4" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 2 + ], + "type": "assignment", + "start_byte": 28545, + "end_byte": 28579 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34, + 3 + ], + "type": "body_statement", + "start_byte": 28812, + "end_byte": 28986 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3 + ], + "type": "then", + "start_byte": 28420, + "end_byte": 28680 + }, + "old_position": 2, + "subtree": true, + "dest_start_byte": 28859, + "dest_end_byte": 28893, + "group_id": "group-4" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 3 + ], + "type": "assignment", + "start_byte": 28590, + "end_byte": 28624 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34, + 3 + ], + "type": "body_statement", + "start_byte": 28812, + "end_byte": 28986 + }, + "position": 2, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3 + ], + "type": "then", + "start_byte": 28420, + "end_byte": 28680 + }, + "old_position": 3, + "subtree": true, + "dest_start_byte": 28900, + "dest_end_byte": 28934, + "group_id": "group-4" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 4 + ], + "type": "call", + "start_byte": 28635, + "end_byte": 28680 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 34, + 3 + ], + "type": "body_statement", + "start_byte": 28812, + "end_byte": 28986 + }, + "position": 3, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3 + ], + "type": "then", + "start_byte": 28420, + "end_byte": 28680 + }, + "old_position": 4, + "subtree": true, + "dest_start_byte": 28941, + "dest_end_byte": 28986, + "group_id": "group-4" + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4 + ], + "type": "when", + "start_byte": 28480, + "end_byte": 28625 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3 + ], + "type": "case", + "start_byte": 27642, + "end_byte": 28726 + }, + "position": 4 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 3, + 0 + ], + "type": "when", + "label": "when", + "start_byte": 28407, + "end_byte": 28411 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 3 + ], + "type": "when", + "start_byte": 28407, + "end_byte": 28473 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 3, + 2 + ], + "type": "then", + "start_byte": 28416, + "end_byte": 28473 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 3 + ], + "type": "when", + "start_byte": 28407, + "end_byte": 28473 + }, + "position": 3, + "subtree": true + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 2 + ], + "type": "pattern", + "start_byte": 28414, + "end_byte": 28420 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4 + ], + "type": "when", + "start_byte": 28480, + "end_byte": 28625 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3 + ], + "type": "when", + "start_byte": 28403, + "end_byte": 28680 + }, + "old_position": 2, + "subtree": true, + "dest_start_byte": 28485, + "dest_end_byte": 28491, + "group_id": "group-2" + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2 + ], + "type": "then", + "start_byte": 28491, + "end_byte": 28625 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4 + ], + "type": "when", + "start_byte": 28480, + "end_byte": 28625 + }, + "position": 2 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 0 + ], + "type": "identifier", + "label": "template_cache", + "start_byte": 27543, + "end_byte": 27557 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2, + 2, + 0 + ], + "type": "call", + "start_byte": 27678, + "end_byte": 28400 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1 + ], + "type": "call", + "start_byte": 27543, + "end_byte": 28797 + }, + "old_position": 0, + "dest_start_byte": 27678, + "dest_end_byte": 27692, + "group_id": "group-5" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 1 + ], + "type": "identifier", + "label": "fetch", + "start_byte": 27558, + "end_byte": 27563 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2, + 2, + 0 + ], + "type": "call", + "start_byte": 27678, + "end_byte": 28400 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1 + ], + "type": "call", + "start_byte": 27543, + "end_byte": 28797 + }, + "old_position": 1, + "dest_start_byte": 27693, + "dest_end_byte": 27698, + "group_id": "group-5" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 2 + ], + "type": "argument_list", + "start_byte": 27564, + "end_byte": 27592 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2, + 2, + 0 + ], + "type": "call", + "start_byte": 27678, + "end_byte": 28400 + }, + "position": 2, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1 + ], + "type": "call", + "start_byte": 27543, + "end_byte": 28797 + }, + "old_position": 2, + "subtree": true, + "dest_start_byte": 27699, + "dest_end_byte": 27727, + "group_id": "group-5" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 0 + ], + "type": "do", + "label": "do", + "start_byte": 27593, + "end_byte": 27595 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2, + 2, + 0, + 3 + ], + "type": "do_block", + "start_byte": 27728, + "end_byte": 28400 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3 + ], + "type": "do_block", + "start_byte": 27593, + "end_byte": 28797 + }, + "old_position": 0, + "dest_start_byte": 27728, + "dest_end_byte": 27730, + "group_id": "group-5" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 2 + ], + "type": "end", + "label": "end", + "start_byte": 28794, + "end_byte": 28797 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2, + 2, + 0, + 3 + ], + "type": "do_block", + "start_byte": 27728, + "end_byte": 28400 + }, + "position": 2, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3 + ], + "type": "do_block", + "start_byte": 27593, + "end_byte": 28797 + }, + "old_position": 2, + "dest_start_byte": 28397, + "dest_end_byte": 28400 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 0 + ], + "type": "identifier", + "label": "template_cache", + "start_byte": 28500, + "end_byte": 28514 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0 + ], + "type": "call", + "start_byte": 28500, + "end_byte": 28625 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 1 + ], + "type": "identifier", + "label": "fetch", + "start_byte": 28515, + "end_byte": 28520 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0 + ], + "type": "call", + "start_byte": 28500, + "end_byte": 28625 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 2 + ], + "type": "argument_list", + "start_byte": 28521, + "end_byte": 28549 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0 + ], + "type": "call", + "start_byte": 28500, + "end_byte": 28625 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3, + 0 + ], + "type": "do", + "label": "do", + "start_byte": 28550, + "end_byte": 28552 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3 + ], + "type": "do_block", + "start_byte": 28550, + "end_byte": 28625 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3, + 2 + ], + "type": "end", + "label": "end", + "start_byte": 28622, + "end_byte": 28625 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3 + ], + "type": "do_block", + "start_byte": 28550, + "end_byte": 28625 + }, + "position": 2 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 2, + 2, + 0 + ], + "type": "assignment", + "start_byte": 27747, + "end_byte": 27790 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2, + 2, + 0, + 3, + 1 + ], + "type": "body_statement", + "start_byte": 27741, + "end_byte": 28388 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 2, + 2 + ], + "type": "then", + "start_byte": 27736, + "end_byte": 28394 + }, + "old_position": 0, + "subtree": true, + "dest_start_byte": 27741, + "dest_end_byte": 27784, + "group_id": "group-6" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 2, + 2, + 1 + ], + "type": "if", + "start_byte": 27801, + "end_byte": 28394 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 2, + 2, + 0, + 3, + 1 + ], + "type": "body_statement", + "start_byte": 27741, + "end_byte": 28388 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 2, + 2 + ], + "type": "then", + "start_byte": 27736, + "end_byte": 28394 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 27795, + "dest_end_byte": 28388, + "group_id": "group-6" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 2, + 2, + 2 + ], + "type": "block", + "start_byte": 28468, + "end_byte": 28476 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3, + 1, + 0 + ], + "type": "call", + "start_byte": 28563, + "end_byte": 28613 + }, + "position": 2, + "old_parent": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 2, + 2 + ], + "type": "call", + "start_byte": 28459, + "end_byte": 28476 + }, + "old_position": 2, + "subtree": true, + "dest_start_byte": 28605, + "dest_end_byte": 28613 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3, + 1, + 0, + 0 + ], + "type": "identifier", + "label": "compile_block_template", + "start_byte": 28563, + "end_byte": 28585 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3, + 1, + 0 + ], + "type": "call", + "start_byte": 28563, + "end_byte": 28613 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3, + 1, + 0, + 1 + ], + "type": "argument_list", + "start_byte": 28585, + "end_byte": 28604 + }, + "parent": { + "tree": "after", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 3, + 4, + 2, + 0, + 3, + 1, + 0 + ], + "type": "call", + "start_byte": 28563, + "end_byte": 28613 + }, + "position": 1, + "subtree": true + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 0 + ], + "type": "when", + "label": "when", + "start_byte": 28403, + "end_byte": 28407 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 0 + ], + "type": "identifier", + "label": "body", + "start_byte": 28431, + "end_byte": 28435 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 1 + ], + "type": "assignment_operator_literal", + "label": "=", + "start_byte": 28436, + "end_byte": 28437 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 2, + 0 + ], + "type": "call", + "start_byte": 28438, + "end_byte": 28456 + }, + "subtree": true + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 2, + 1 + ], + "type": "?", + "label": "?", + "start_byte": 28457, + "end_byte": 28458 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 2, + 3 + ], + "type": "identifier", + "label": "data", + "start_byte": 28479, + "end_byte": 28483 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3 + ], + "type": "then", + "start_byte": 28420, + "end_byte": 28680 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 17, + 4, + 37, + 2, + 32, + 3, + 1, + 3, + 1, + 2, + 3, + 3, + 0, + 2, + 2, + 0 + ], + "type": "constant", + "label": "Proc", + "start_byte": 28459, + "end_byte": 28463 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": 277, + "right_line": 277 + }, + { + "left_line": 278, + "right_line": 278 + }, + { + "left_line": 279, + "right_line": 279 + }, + { + "left_line": 280, + "right_line": 280 + }, + { + "left_line": 281, + "right_line": 281 + }, + { + "left_line": 282, + "right_line": 282 + }, + { + "left_line": 283, + "right_line": 283 + }, + { + "left_line": 284, + "right_line": 284 + }, + { + "left_line": 285, + "right_line": 285 + }, + { + "left_line": 286, + "right_line": 286 + }, + { + "left_line": 287, + "right_line": 287 + }, + { + "left_line": 288, + "right_line": 288 + }, + { + "left_line": 289, + "right_line": 289 + }, + { + "left_line": 290, + "right_line": 290 + }, + { + "left_line": 291, + "right_line": 291 + }, + { + "left_line": 292, + "right_line": 292 + }, + { + "left_line": 293, + "right_line": 293 + }, + { + "left_line": 294, + "right_line": 294 + }, + { + "left_line": 295, + "right_line": 295 + }, + { + "left_line": 296, + "right_line": 296 + }, + { + "left_line": 297, + "right_line": 297 + }, + { + "left_line": 298, + "right_line": 298 + }, + { + "left_line": 299, + "right_line": 299 + }, + { + "left_line": 300, + "right_line": 300 + }, + { + "left_line": 301, + "right_line": 301 + }, + { + "left_line": 302, + "right_line": 302 + }, + { + "left_line": 303, + "right_line": 303 + }, + { + "left_line": 304, + "right_line": 304 + }, + { + "left_line": 305, + "right_line": 305 + }, + { + "left_line": 306, + "right_line": 306 + }, + { + "left_line": 307, + "right_line": 307 + }, + { + "left_line": 308, + "right_line": 308 + }, + { + "left_line": 309, + "right_line": 309 + }, + { + "left_line": 310, + "right_line": 310 + }, + { + "left_line": 311, + "right_line": 311 + }, + { + "left_line": 312, + "right_line": 312 + }, + { + "left_line": 313, + "right_line": 313 + }, + { + "left_line": 314, + "right_line": 314 + }, + { + "left_line": 315, + "right_line": 315 + }, + { + "left_line": 316, + "right_line": 316 + }, + { + "left_line": 317, + "right_line": 317 + }, + { + "left_line": 318, + "right_line": 318 + }, + { + "left_line": 319, + "right_line": 319 + }, + { + "left_line": 320, + "right_line": 320 + }, + { + "left_line": 321, + "right_line": 321 + }, + { + "left_line": 322, + "right_line": 322 + }, + { + "left_line": 323, + "right_line": 323 + }, + { + "left_line": 324, + "right_line": 324 + }, + { + "left_line": 325, + "right_line": 325 + }, + { + "left_line": 326, + "right_line": 326 + }, + { + "left_line": 327, + "right_line": 327 + }, + { + "left_line": 328, + "right_line": 328 + }, + { + "left_line": 329, + "right_line": 329 + }, + { + "left_line": 330, + "right_line": 330 + }, + { + "left_line": 331, + "right_line": 331 + }, + { + "left_line": 332, + "right_line": 332 + }, + { + "left_line": 333, + "right_line": 333 + }, + { + "left_line": 334, + "right_line": 334 + }, + { + "left_line": 335, + "right_line": 335 + }, + { + "left_line": 336, + "right_line": 336 + }, + { + "left_line": 337, + "right_line": 337 + }, + { + "left_line": 338, + "right_line": 338 + }, + { + "left_line": 339, + "right_line": 339 + }, + { + "left_line": 340, + "right_line": 340 + }, + { + "left_line": 341, + "right_line": 341 + }, + { + "left_line": 342, + "right_line": 342 + }, + { + "left_line": 343, + "right_line": 343 + }, + { + "left_line": 344, + "right_line": 344 + }, + { + "left_line": 345, + "right_line": 345 + }, + { + "left_line": 346, + "right_line": 346 + }, + { + "left_line": 347, + "right_line": 347 + }, + { + "left_line": 348, + "right_line": 348 + }, + { + "left_line": 349, + "right_line": 349 + }, + { + "left_line": 350, + "right_line": 350 + }, + { + "left_line": 351, + "right_line": 351 + }, + { + "left_line": 352, + "right_line": 352 + }, + { + "left_line": 353, + "right_line": 353 + }, + { + "left_line": 354, + "right_line": 354 + }, + { + "left_line": 355, + "right_line": 355 + }, + { + "left_line": 356, + "right_line": 356 + }, + { + "left_line": 357, + "right_line": 357 + }, + { + "left_line": 358, + "right_line": 358 + }, + { + "left_line": 359, + "right_line": 359 + }, + { + "left_line": 360, + "right_line": 360 + }, + { + "left_line": 361, + "right_line": 361 + }, + { + "left_line": 362, + "right_line": 362 + }, + { + "left_line": 363, + "right_line": 363 + }, + { + "left_line": 364, + "right_line": 364 + }, + { + "left_line": 365, + "right_line": 365 + }, + { + "left_line": 366, + "right_line": 366 + }, + { + "left_line": 367, + "right_line": 367 + }, + { + "left_line": 368, + "right_line": 368 + }, + { + "left_line": 369, + "right_line": 369 + }, + { + "left_line": 370, + "right_line": 370 + }, + { + "left_line": 371, + "right_line": 371 + }, + { + "left_line": 372, + "right_line": 372 + }, + { + "left_line": 373, + "right_line": 373 + }, + { + "left_line": 374, + "right_line": 374 + }, + { + "left_line": 375, + "right_line": 375 + }, + { + "left_line": 376, + "right_line": 376 + }, + { + "left_line": 377, + "right_line": 377 + }, + { + "left_line": 378, + "right_line": 378 + }, + { + "left_line": 379, + "right_line": 379 + }, + { + "left_line": 380, + "right_line": 380 + }, + { + "left_line": 381, + "right_line": 381 + }, + { + "left_line": 382, + "right_line": 382 + }, + { + "left_line": 383, + "right_line": 383 + }, + { + "left_line": 384, + "right_line": 384 + }, + { + "left_line": 385, + "right_line": 385 + }, + { + "left_line": 386, + "right_line": 386 + }, + { + "left_line": 387, + "right_line": 387 + }, + { + "left_line": 388, + "right_line": 388 + }, + { + "left_line": 389, + "right_line": 389 + }, + { + "left_line": 390, + "right_line": 390 + }, + { + "left_line": 391, + "right_line": 391 + }, + { + "left_line": 392, + "right_line": 392 + }, + { + "left_line": 393, + "right_line": 393 + }, + { + "left_line": 394, + "right_line": 394 + }, + { + "left_line": 395, + "right_line": 395 + }, + { + "left_line": 396, + "right_line": 396 + }, + { + "left_line": 397, + "right_line": 397 + }, + { + "left_line": 398, + "right_line": 398 + }, + { + "left_line": 399, + "right_line": 399 + }, + { + "left_line": 400, + "right_line": 400 + }, + { + "left_line": 401, + "right_line": 401 + }, + { + "left_line": 402, + "right_line": 402 + }, + { + "left_line": 403, + "right_line": 403 + }, + { + "left_line": 404, + "right_line": 404 + }, + { + "left_line": 405, + "right_line": 405 + }, + { + "left_line": 406, + "right_line": 406 + }, + { + "left_line": 407, + "right_line": 407 + }, + { + "left_line": 408, + "right_line": 408 + }, + { + "left_line": 409, + "right_line": 409 + }, + { + "left_line": 410, + "right_line": 410 + }, + { + "left_line": 411, + "right_line": 411 + }, + { + "left_line": 412, + "right_line": 412 + }, + { + "left_line": 413, + "right_line": 413 + }, + { + "left_line": 414, + "right_line": 414 + }, + { + "left_line": 415, + "right_line": 415 + }, + { + "left_line": 416, + "right_line": 416 + }, + { + "left_line": 417, + "right_line": 417 + }, + { + "left_line": 418, + "right_line": 418 + }, + { + "left_line": 419, + "right_line": 419 + }, + { + "left_line": 420, + "right_line": 420 + }, + { + "left_line": 421, + "right_line": 421 + }, + { + "left_line": 422, + "right_line": 422 + }, + { + "left_line": 423, + "right_line": 423 + }, + { + "left_line": 424, + "right_line": 424 + }, + { + "left_line": 425, + "right_line": 425 + }, + { + "left_line": 426, + "right_line": 426 + }, + { + "left_line": 427, + "right_line": 427 + }, + { + "left_line": 428, + "right_line": 428 + }, + { + "left_line": 429, + "right_line": 429 + }, + { + "left_line": 430, + "right_line": 430 + }, + { + "left_line": 431, + "right_line": 431 + }, + { + "left_line": 432, + "right_line": 432 + }, + { + "left_line": 433, + "right_line": 433 + }, + { + "left_line": 434, + "right_line": 434 + }, + { + "left_line": 435, + "right_line": 435 + }, + { + "left_line": 436, + "right_line": 436 + }, + { + "left_line": 437, + "right_line": 437 + }, + { + "left_line": 438, + "right_line": 438 + }, + { + "left_line": 439, + "right_line": 439 + }, + { + "left_line": 440, + "right_line": 440 + }, + { + "left_line": 441, + "right_line": 441 + }, + { + "left_line": 442, + "right_line": 442 + }, + { + "left_line": 443, + "right_line": 443 + }, + { + "left_line": 444, + "right_line": 444 + }, + { + "left_line": 445, + "right_line": 445 + }, + { + "left_line": 446, + "right_line": 446 + }, + { + "left_line": 447, + "right_line": 447 + }, + { + "left_line": 448, + "right_line": 448 + }, + { + "left_line": 449, + "right_line": 449 + }, + { + "left_line": 450, + "right_line": 450 + }, + { + "left_line": 451, + "right_line": 451 + }, + { + "left_line": 452, + "right_line": 452 + }, + { + "left_line": 453, + "right_line": 453 + }, + { + "left_line": 454, + "right_line": 454 + }, + { + "left_line": 455, + "right_line": 455 + }, + { + "left_line": 456, + "right_line": 456 + }, + { + "left_line": 457, + "right_line": 457 + }, + { + "left_line": 458, + "right_line": 458 + }, + { + "left_line": 459, + "right_line": 459 + }, + { + "left_line": 460, + "right_line": 460 + }, + { + "left_line": 461, + "right_line": 461 + }, + { + "left_line": 462, + "right_line": 462 + }, + { + "left_line": 463, + "right_line": 463 + }, + { + "left_line": 464, + "right_line": 464 + }, + { + "left_line": 465, + "right_line": 465 + }, + { + "left_line": 466, + "right_line": 466 + }, + { + "left_line": 467, + "right_line": 467 + }, + { + "left_line": 468, + "right_line": 468 + }, + { + "left_line": 469, + "right_line": 469 + }, + { + "left_line": 470, + "right_line": 470 + }, + { + "left_line": 471, + "right_line": 471 + }, + { + "left_line": 472, + "right_line": 472 + }, + { + "left_line": 473, + "right_line": 473 + }, + { + "left_line": 474, + "right_line": 474 + }, + { + "left_line": 475, + "right_line": 475 + }, + { + "left_line": 476, + "right_line": 476 + }, + { + "left_line": 477, + "right_line": 477 + }, + { + "left_line": 478, + "right_line": 478 + }, + { + "left_line": 479, + "right_line": 479 + }, + { + "left_line": 480, + "right_line": 480 + }, + { + "left_line": 481, + "right_line": 481 + }, + { + "left_line": 482, + "right_line": 482 + }, + { + "left_line": 483, + "right_line": 483 + }, + { + "left_line": 484, + "right_line": 484 + }, + { + "left_line": 485, + "right_line": 485 + }, + { + "left_line": 486, + "right_line": 486 + }, + { + "left_line": 487, + "right_line": 487 + }, + { + "left_line": 488, + "right_line": 488 + }, + { + "left_line": 489, + "right_line": 489 + }, + { + "left_line": 490, + "right_line": 490 + }, + { + "left_line": 491, + "right_line": 491 + }, + { + "left_line": 492, + "right_line": 492 + }, + { + "left_line": 493, + "right_line": 493 + }, + { + "left_line": 494, + "right_line": 494 + }, + { + "left_line": 495, + "right_line": 495 + }, + { + "left_line": 496, + "right_line": 496 + }, + { + "left_line": 497, + "right_line": 497 + }, + { + "left_line": 498, + "right_line": 498 + }, + { + "left_line": 499, + "right_line": 499 + }, + { + "left_line": 500, + "right_line": 500 + }, + { + "left_line": 501, + "right_line": 501 + }, + { + "left_line": 502, + "right_line": 502 + }, + { + "left_line": 503, + "right_line": 503 + }, + { + "left_line": 504, + "right_line": 504 + }, + { + "left_line": 505, + "right_line": 505 + }, + { + "left_line": 506, + "right_line": 506 + }, + { + "left_line": 507, + "right_line": 507 + }, + { + "left_line": 508, + "right_line": 508 + }, + { + "left_line": 509, + "right_line": 509 + }, + { + "left_line": 510, + "right_line": 510 + }, + { + "left_line": 511, + "right_line": 511 + }, + { + "left_line": 512, + "right_line": 512 + }, + { + "left_line": 513, + "right_line": 513 + }, + { + "left_line": 514, + "right_line": 514 + }, + { + "left_line": 515, + "right_line": 515 + }, + { + "left_line": 516, + "right_line": 516 + }, + { + "left_line": 517, + "right_line": 517 + }, + { + "left_line": 518, + "right_line": 518 + }, + { + "left_line": 519, + "right_line": 519 + }, + { + "left_line": 520, + "right_line": 520 + }, + { + "left_line": 521, + "right_line": 521 + }, + { + "left_line": 522, + "right_line": 522 + }, + { + "left_line": 523, + "right_line": 523 + }, + { + "left_line": 524, + "right_line": 524 + }, + { + "left_line": 525, + "right_line": 525 + }, + { + "left_line": 526, + "right_line": 526 + }, + { + "left_line": 527, + "right_line": 527 + }, + { + "left_line": 528, + "right_line": 528 + }, + { + "left_line": 529, + "right_line": 529 + }, + { + "left_line": 530, + "right_line": 530 + }, + { + "left_line": 531, + "right_line": 531 + }, + { + "left_line": 532, + "right_line": 532 + }, + { + "left_line": 533, + "right_line": 533 + }, + { + "left_line": 534, + "right_line": 534 + }, + { + "left_line": 535, + "right_line": 535 + }, + { + "left_line": 536, + "right_line": 536 + }, + { + "left_line": 537, + "right_line": 537 + }, + { + "left_line": 538, + "right_line": 538 + }, + { + "left_line": 539, + "right_line": 539 + }, + { + "left_line": 540, + "right_line": 540 + }, + { + "left_line": 541, + "right_line": 541 + }, + { + "left_line": 542, + "right_line": 542 + }, + { + "left_line": 543, + "right_line": 543 + }, + { + "left_line": 544, + "right_line": 544 + }, + { + "left_line": 545, + "right_line": 545 + }, + { + "left_line": 546, + "right_line": 546 + }, + { + "left_line": 547, + "right_line": 547 + }, + { + "left_line": 548, + "right_line": 548 + }, + { + "left_line": 549, + "right_line": 549 + }, + { + "left_line": 550, + "right_line": 550 + }, + { + "left_line": 551, + "right_line": 551 + }, + { + "left_line": 552, + "right_line": 552 + }, + { + "left_line": 553, + "right_line": 553 + }, + { + "left_line": 554, + "right_line": 554 + }, + { + "left_line": 555, + "right_line": 555 + }, + { + "left_line": 556, + "right_line": 556 + }, + { + "left_line": 557, + "right_line": 557 + }, + { + "left_line": 558, + "right_line": 558 + }, + { + "left_line": 559, + "right_line": 559 + }, + { + "left_line": 560, + "right_line": 560 + }, + { + "left_line": 561, + "right_line": 561 + }, + { + "left_line": 562, + "right_line": 562 + }, + { + "left_line": 563, + "right_line": 563 + }, + { + "left_line": 564, + "right_line": 564 + }, + { + "left_line": 565, + "right_line": 565 + }, + { + "left_line": 566, + "right_line": 566 + }, + { + "left_line": 567, + "right_line": 567 + }, + { + "left_line": 568, + "right_line": 568 + }, + { + "left_line": 569, + "right_line": 569 + }, + { + "left_line": 570, + "right_line": 570 + }, + { + "left_line": 571, + "right_line": 571 + }, + { + "left_line": 572, + "right_line": 572 + }, + { + "left_line": 573, + "right_line": 573 + }, + { + "left_line": 574, + "right_line": 574 + }, + { + "left_line": 575, + "right_line": 575 + }, + { + "left_line": 576, + "right_line": 576 + }, + { + "left_line": 577, + "right_line": 577 + }, + { + "left_line": 578, + "right_line": 578 + }, + { + "left_line": 579, + "right_line": 579 + }, + { + "left_line": 580, + "right_line": 580 + }, + { + "left_line": 581, + "right_line": 581 + }, + { + "left_line": 582, + "right_line": 582 + }, + { + "left_line": 583, + "right_line": 583 + }, + { + "left_line": 584, + "right_line": 584 + }, + { + "left_line": 585, + "right_line": 585 + }, + { + "left_line": 586, + "right_line": 586 + }, + { + "left_line": 587, + "right_line": 587 + }, + { + "left_line": 588, + "right_line": 588 + }, + { + "left_line": 589, + "right_line": 589 + }, + { + "left_line": 590, + "right_line": 590 + }, + { + "left_line": 591, + "right_line": 591 + }, + { + "left_line": 592, + "right_line": 592 + }, + { + "left_line": 593, + "right_line": 593 + }, + { + "left_line": 594, + "right_line": 594 + }, + { + "left_line": 595, + "right_line": 595 + }, + { + "left_line": 596, + "right_line": 596 + }, + { + "left_line": 597, + "right_line": 597 + }, + { + "left_line": 598, + "right_line": 598 + }, + { + "left_line": 599, + "right_line": 599 + }, + { + "left_line": 600, + "right_line": 600 + }, + { + "left_line": 601, + "right_line": 601 + }, + { + "left_line": 602, + "right_line": 602 + }, + { + "left_line": 603, + "right_line": 603 + }, + { + "left_line": 604, + "right_line": 604 + }, + { + "left_line": 605, + "right_line": 605 + }, + { + "left_line": 606, + "right_line": 606 + }, + { + "left_line": 607, + "right_line": 607 + }, + { + "left_line": 608, + "right_line": 608 + }, + { + "left_line": 609, + "right_line": 609 + }, + { + "left_line": 610, + "right_line": 610 + }, + { + "left_line": 611, + "right_line": 611 + }, + { + "left_line": 612, + "right_line": 612 + }, + { + "left_line": 613, + "right_line": 613 + }, + { + "left_line": 614, + "right_line": 614 + }, + { + "left_line": 615, + "right_line": 615 + }, + { + "left_line": 616, + "right_line": 616 + }, + { + "left_line": 617, + "right_line": 617 + }, + { + "left_line": 618, + "right_line": 618 + }, + { + "left_line": 619, + "right_line": 619 + }, + { + "left_line": 620, + "right_line": 620 + }, + { + "left_line": 621, + "right_line": 621 + }, + { + "left_line": 622, + "right_line": 622 + }, + { + "left_line": 623, + "right_line": 623 + }, + { + "left_line": 624, + "right_line": 624 + }, + { + "left_line": 625, + "right_line": 625 + }, + { + "left_line": 626, + "right_line": 626 + }, + { + "left_line": 627, + "right_line": 627 + }, + { + "left_line": 628, + "right_line": 628 + }, + { + "left_line": 629, + "right_line": 629 + }, + { + "left_line": 630, + "right_line": 630 + }, + { + "left_line": 631, + "right_line": 631 + }, + { + "left_line": 632, + "right_line": 632 + }, + { + "left_line": 633, + "right_line": 633 + }, + { + "left_line": 634, + "right_line": 634 + }, + { + "left_line": 635, + "right_line": 635 + }, + { + "left_line": 636, + "right_line": 636 + }, + { + "left_line": 637, + "right_line": 637 + }, + { + "left_line": 638, + "right_line": 638 + }, + { + "left_line": 639, + "right_line": 639 + }, + { + "left_line": 640, + "right_line": 640 + }, + { + "left_line": 641, + "right_line": 641 + }, + { + "left_line": 642, + "right_line": 642 + }, + { + "left_line": 643, + "right_line": 643 + }, + { + "left_line": 644, + "right_line": 644 + }, + { + "left_line": 645, + "right_line": 645 + }, + { + "left_line": 646, + "right_line": 646 + }, + { + "left_line": 647, + "right_line": 647 + }, + { + "left_line": 648, + "right_line": 648 + }, + { + "left_line": 649, + "right_line": 649 + }, + { + "left_line": 650, + "right_line": 650 + }, + { + "left_line": 651, + "right_line": 651 + }, + { + "left_line": 652, + "right_line": 652 + }, + { + "left_line": 653, + "right_line": 653 + }, + { + "left_line": 654, + "right_line": 654 + }, + { + "left_line": 655, + "right_line": 655 + }, + { + "left_line": 656, + "right_line": 656 + }, + { + "left_line": 657, + "right_line": 657 + }, + { + "left_line": 658, + "right_line": 658 + }, + { + "left_line": 659, + "right_line": 659 + }, + { + "left_line": 660, + "right_line": 660 + }, + { + "left_line": 661, + "right_line": 661 + }, + { + "left_line": 662, + "right_line": 662 + }, + { + "left_line": 663, + "right_line": 663 + }, + { + "left_line": 664, + "right_line": 664 + }, + { + "left_line": 665, + "right_line": 665 + }, + { + "left_line": 666, + "right_line": 666 + }, + { + "left_line": 667, + "right_line": 667 + }, + { + "left_line": 668, + "right_line": 668 + }, + { + "left_line": 669, + "right_line": 669 + }, + { + "left_line": 670, + "right_line": 670 + }, + { + "left_line": 671, + "right_line": 671 + }, + { + "left_line": 672, + "right_line": 672 + }, + { + "left_line": 673, + "right_line": 673 + }, + { + "left_line": 674, + "right_line": 674 + }, + { + "left_line": 675, + "right_line": 675 + }, + { + "left_line": 676, + "right_line": 676 + }, + { + "left_line": 677, + "right_line": 677 + }, + { + "left_line": 678, + "right_line": 678 + }, + { + "left_line": 679, + "right_line": 679 + }, + { + "left_line": 680, + "right_line": 680 + }, + { + "left_line": 681, + "right_line": 681 + }, + { + "left_line": 682, + "right_line": 682 + }, + { + "left_line": 683, + "right_line": 683 + }, + { + "left_line": 684, + "right_line": 684 + }, + { + "left_line": 685, + "right_line": 685 + }, + { + "left_line": 686, + "right_line": 686 + }, + { + "left_line": 687, + "right_line": 687 + }, + { + "left_line": 688, + "right_line": 688 + }, + { + "left_line": 689, + "right_line": 689 + }, + { + "left_line": 690, + "right_line": 690 + }, + { + "left_line": 691, + "right_line": 691 + }, + { + "left_line": 692, + "right_line": 692 + }, + { + "left_line": 693, + "right_line": 693 + }, + { + "left_line": 694, + "right_line": 694 + }, + { + "left_line": 695, + "right_line": 695 + }, + { + "left_line": 696, + "right_line": 696 + }, + { + "left_line": 697, + "right_line": 697 + }, + { + "left_line": 698, + "right_line": 698 + }, + { + "left_line": 699, + "right_line": 699 + }, + { + "left_line": 700, + "right_line": 700 + }, + { + "left_line": 701, + "right_line": 701 + }, + { + "left_line": 702, + "right_line": 702 + }, + { + "left_line": 703, + "right_line": 703 + }, + { + "left_line": 704, + "right_line": 704 + }, + { + "left_line": 705, + "right_line": 705 + }, + { + "left_line": 706, + "right_line": 706 + }, + { + "left_line": 707, + "right_line": 707 + }, + { + "left_line": 708, + "right_line": 708 + }, + { + "left_line": 709, + "right_line": 709 + }, + { + "left_line": 710, + "right_line": 710 + }, + { + "left_line": 711, + "right_line": 711 + }, + { + "left_line": 712, + "right_line": 712 + }, + { + "left_line": 713, + "right_line": 713 + }, + { + "left_line": 714, + "right_line": 714 + }, + { + "left_line": 715, + "right_line": 715 + }, + { + "left_line": 716, + "right_line": 716 + }, + { + "left_line": 717, + "right_line": 717 + }, + { + "left_line": 718, + "right_line": 718 + }, + { + "left_line": 719, + "right_line": 719 + }, + { + "left_line": 720, + "right_line": 720 + }, + { + "left_line": 721, + "right_line": 721 + }, + { + "left_line": 722, + "right_line": 722 + }, + { + "left_line": 723, + "right_line": 723 + }, + { + "left_line": 724, + "right_line": 724 + }, + { + "left_line": 725, + "right_line": 725 + }, + { + "left_line": 726, + "right_line": 726 + }, + { + "left_line": 727, + "right_line": 727 + }, + { + "left_line": 728, + "right_line": 728 + }, + { + "left_line": 729, + "right_line": 729 + }, + { + "left_line": 730, + "right_line": 730 + }, + { + "left_line": 731, + "right_line": 731 + }, + { + "left_line": 732, + "right_line": 732 + }, + { + "left_line": 733, + "right_line": 733 + }, + { + "left_line": 734, + "right_line": 734 + }, + { + "left_line": 735, + "right_line": 735 + }, + { + "left_line": 736, + "right_line": 736 + }, + { + "left_line": 737, + "right_line": 737 + }, + { + "left_line": 738, + "right_line": 738 + }, + { + "left_line": 739, + "right_line": 739 + }, + { + "left_line": 740, + "right_line": 740 + }, + { + "left_line": 741, + "right_line": 741 + }, + { + "left_line": 742, + "right_line": 742 + }, + { + "left_line": 743, + "right_line": 743 + }, + { + "left_line": 744, + "right_line": 744 + }, + { + "left_line": 745, + "right_line": 745 + }, + { + "left_line": 746, + "right_line": 746 + }, + { + "left_line": 747, + "right_line": 747 + }, + { + "left_line": 748, + "right_line": 748 + }, + { + "left_line": 749, + "right_line": 749 + }, + { + "left_line": 750, + "right_line": 750 + }, + { + "left_line": 751, + "right_line": 751 + }, + { + "left_line": 752, + "right_line": 752 + }, + { + "left_line": 753, + "right_line": 753 + }, + { + "left_line": 754, + "right_line": 754 + }, + { + "left_line": 755, + "right_line": 755 + }, + { + "left_line": 756, + "right_line": 756 + }, + { + "left_line": 757, + "right_line": 757 + }, + { + "left_line": 758, + "right_line": 758 + }, + { + "left_line": 759, + "right_line": 759 + }, + { + "left_line": 760, + "right_line": 760 + }, + { + "left_line": 761, + "right_line": 761 + }, + { + "left_line": 762, + "right_line": 762 + }, + { + "left_line": 763, + "right_line": 763 + }, + { + "left_line": 764, + "right_line": 764 + }, + { + "left_line": 765, + "right_line": 765 + }, + { + "left_line": 766, + "right_line": 766 + }, + { + "left_line": 767, + "right_line": 767 + }, + { + "left_line": 768, + "right_line": 768 + }, + { + "left_line": 769, + "right_line": 769 + }, + { + "left_line": 770, + "right_line": 770 + }, + { + "left_line": 771, + "right_line": 771 + }, + { + "left_line": 772, + "right_line": 772 + }, + { + "left_line": 773, + "right_line": 773 + }, + { + "left_line": 774, + "right_line": 774 + }, + { + "left_line": 775, + "right_line": 775 + }, + { + "left_line": 776, + "right_line": 776 + }, + { + "left_line": 777, + "right_line": 777 + }, + { + "left_line": 778, + "right_line": 778 + }, + { + "left_line": 779, + "right_line": 779 + }, + { + "left_line": 780, + "right_line": 780 + }, + { + "left_line": 781, + "right_line": 781 + }, + { + "left_line": 782, + "right_line": 782 + }, + { + "left_line": 783, + "right_line": 783 + }, + { + "left_line": 784, + "right_line": 784 + }, + { + "left_line": 785, + "right_line": 785 + }, + { + "left_line": 786, + "right_line": 786 + }, + { + "left_line": 787, + "right_line": 787 + }, + { + "left_line": 788, + "right_line": 788 + }, + { + "left_line": 789, + "right_line": 789 + }, + { + "left_line": 790, + "right_line": 790 + }, + { + "left_line": 791, + "right_line": 791 + }, + { + "left_line": 792, + "right_line": 792 + }, + { + "left_line": 793, + "right_line": 793 + }, + { + "left_line": 794, + "right_line": 794 + }, + { + "left_line": 795, + "right_line": 795 + }, + { + "left_line": 796, + "right_line": 796 + }, + { + "left_line": 797, + "right_line": 797 + }, + { + "left_line": 798, + "right_line": 798 + }, + { + "left_line": 799, + "right_line": 799 + }, + { + "left_line": 800, + "right_line": 800 + }, + { + "left_line": 801, + "right_line": 801 + }, + { + "left_line": 802, + "right_line": 802 + }, + { + "left_line": 803, + "right_line": 803 + }, + { + "left_line": 804, + "right_line": 804 + }, + { + "left_line": 805, + "right_line": 805 + }, + { + "left_line": 806, + "right_line": 806 + }, + { + "left_line": 807, + "right_line": 807 + }, + { + "left_line": 808, + "right_line": 808 + }, + { + "left_line": 809, + "right_line": 809 + }, + { + "left_line": 810, + "right_line": 810 + }, + { + "left_line": 811, + "right_line": 811 + }, + { + "left_line": 812, + "right_line": 812 + }, + { + "left_line": 813, + "right_line": 813 + }, + { + "left_line": 814, + "right_line": 814 + }, + { + "left_line": 815, + "right_line": 815 + }, + { + "left_line": 816, + "right_line": 816 + }, + { + "left_line": 817, + "right_line": 817 + }, + { + "left_line": 818, + "right_line": 818 + }, + { + "left_line": 819, + "right_line": 819 + }, + { + "left_line": 820, + "right_line": 820 + }, + { + "left_line": 821, + "right_line": 821 + }, + { + "left_line": 822, + "right_line": 822 + }, + { + "left_line": 823, + "right_line": 823 + }, + { + "left_line": 824, + "right_line": 824 + }, + { + "left_line": 825, + "right_line": 825 + }, + { + "left_line": 826, + "right_line": 826 + }, + { + "left_line": 827, + "right_line": 827 + }, + { + "left_line": 828, + "right_line": 828 + }, + { + "left_line": 829, + "right_line": 829 + }, + { + "left_line": 830, + "right_line": 830 + }, + { + "left_line": 831, + "right_line": 831 + }, + { + "left_line": 832, + "right_line": 832 + }, + { + "left_line": 833, + "right_line": 833 + }, + { + "left_line": 834, + "right_line": 834 + }, + { + "left_line": 835, + "right_line": 835 + }, + { + "left_line": 836, + "right_line": 836 + }, + { + "left_line": 837, + "right_line": 837 + }, + { + "left_line": 838, + "right_line": 838 + }, + { + "left_line": 839, + "right_line": 839 + }, + { + "left_line": 840, + "right_line": 840 + }, + { + "left_line": 841, + "right_line": 841 + }, + { + "left_line": 842, + "right_line": 842 + }, + { + "left_line": 843, + "right_line": 843 + }, + { + "left_line": 844, + "right_line": 844 + }, + { + "left_line": 845, + "right_line": 845 + }, + { + "left_line": 846, + "right_line": 846 + }, + { + "left_line": 847, + "right_line": 847 + }, + { + "left_line": 848, + "right_line": 848 + }, + { + "left_line": 849, + "right_line": 849 + }, + { + "left_line": 850, + "right_line": 850 + }, + { + "left_line": 851, + "right_line": 851 + }, + { + "left_line": 852, + "right_line": 852 + }, + { + "left_line": 853, + "right_line": 853 + }, + { + "left_line": 854, + "right_line": 854 + }, + { + "left_line": 855, + "right_line": 855 + }, + { + "left_line": 856, + "right_line": 856 + }, + { + "left_line": 857, + "right_line": 857 + }, + { + "left_line": 858, + "right_line": 858 + }, + { + "left_line": 859, + "right_line": 859 + }, + { + "left_line": 860, + "right_line": 860 + }, + { + "left_line": 861, + "right_line": 861 + }, + { + "left_line": 862, + "right_line": 862 + }, + { + "left_line": 863, + "right_line": 863 + }, + { + "left_line": 864, + "right_line": 864 + }, + { + "left_line": 865, + "right_line": 865 + }, + { + "left_line": 866, + "right_line": 866 + }, + { + "left_line": 867, + "right_line": 867 + }, + { + "left_line": 868, + "right_line": 868 + }, + { + "left_line": 869, + "right_line": 869 + }, + { + "left_line": 870, + "right_line": 870 + }, + { + "left_line": 871, + "right_line": -1 + }, + { + "left_line": 872, + "right_line": 871 + }, + { + "left_line": 873, + "right_line": 872 + }, + { + "left_line": 874, + "right_line": 873 + }, + { + "left_line": 875, + "right_line": 874 + }, + { + "left_line": 876, + "right_line": 875 + }, + { + "left_line": -1, + "right_line": 876 + }, + { + "left_line": 877, + "right_line": 877 + }, + { + "left_line": 878, + "right_line": 878 + }, + { + "left_line": 879, + "right_line": 879 + }, + { + "left_line": 880, + "right_line": 880 + }, + { + "left_line": 881, + "right_line": 881 + }, + { + "left_line": 882, + "right_line": 882 + }, + { + "left_line": 883, + "right_line": 883 + }, + { + "left_line": 884, + "right_line": 884 + }, + { + "left_line": 885, + "right_line": 885 + }, + { + "left_line": 886, + "right_line": 886 + }, + { + "left_line": 887, + "right_line": 887 + }, + { + "left_line": 888, + "right_line": 888 + }, + { + "left_line": 889, + "right_line": 889 + }, + { + "left_line": 890, + "right_line": 890 + }, + { + "left_line": 891, + "right_line": 891 + }, + { + "left_line": 892, + "right_line": 892 + }, + { + "left_line": 893, + "right_line": 893 + }, + { + "left_line": 894, + "right_line": -1 + }, + { + "left_line": -1, + "right_line": 894 + }, + { + "left_line": -1, + "right_line": 895 + }, + { + "left_line": -1, + "right_line": 896 + }, + { + "left_line": -1, + "right_line": 897 + }, + { + "left_line": -1, + "right_line": 898 + }, + { + "left_line": 895, + "right_line": 899 + }, + { + "left_line": 896, + "right_line": -1 + }, + { + "left_line": 897, + "right_line": -1 + }, + { + "left_line": 898, + "right_line": -1 + }, + { + "left_line": 899, + "right_line": -1 + }, + { + "left_line": 900, + "right_line": -1 + }, + { + "left_line": 901, + "right_line": -1 + }, + { + "left_line": 902, + "right_line": 900 + }, + { + "left_line": -1, + "right_line": 901 + }, + { + "left_line": -1, + "right_line": 902 + }, + { + "left_line": 903, + "right_line": 903 + }, + { + "left_line": 904, + "right_line": 904 + }, + { + "left_line": -1, + "right_line": 905 + }, + { + "left_line": -1, + "right_line": 906 + }, + { + "left_line": -1, + "right_line": 907 + }, + { + "left_line": -1, + "right_line": 908 + }, + { + "left_line": -1, + "right_line": 909 + }, + { + "left_line": -1, + "right_line": 910 + }, + { + "left_line": -1, + "right_line": 911 + }, + { + "left_line": -1, + "right_line": 912 + }, + { + "left_line": -1, + "right_line": 913 + }, + { + "left_line": 905, + "right_line": 914 + }, + { + "left_line": 906, + "right_line": 915 + }, + { + "left_line": 907, + "right_line": 916 + }, + { + "left_line": 908, + "right_line": 917 + }, + { + "left_line": 909, + "right_line": 918 + }, + { + "left_line": 910, + "right_line": 919 + }, + { + "left_line": 911, + "right_line": 920 + }, + { + "left_line": 912, + "right_line": 921 + }, + { + "left_line": 913, + "right_line": 922 + }, + { + "left_line": 914, + "right_line": 923 + }, + { + "left_line": 915, + "right_line": 924 + }, + { + "left_line": 916, + "right_line": 925 + }, + { + "left_line": 917, + "right_line": 926 + }, + { + "left_line": 918, + "right_line": 927 + }, + { + "left_line": 919, + "right_line": 928 + }, + { + "left_line": 920, + "right_line": 929 + }, + { + "left_line": 921, + "right_line": 930 + }, + { + "left_line": 922, + "right_line": 931 + }, + { + "left_line": 923, + "right_line": 932 + }, + { + "left_line": 924, + "right_line": 933 + }, + { + "left_line": 925, + "right_line": 934 + }, + { + "left_line": 926, + "right_line": 935 + }, + { + "left_line": 927, + "right_line": 936 + }, + { + "left_line": 928, + "right_line": 937 + }, + { + "left_line": 929, + "right_line": 938 + }, + { + "left_line": 930, + "right_line": 939 + }, + { + "left_line": 931, + "right_line": 940 + }, + { + "left_line": 932, + "right_line": 941 + }, + { + "left_line": 933, + "right_line": 942 + }, + { + "left_line": 934, + "right_line": 943 + }, + { + "left_line": 935, + "right_line": 944 + }, + { + "left_line": 936, + "right_line": 945 + }, + { + "left_line": 937, + "right_line": 946 + }, + { + "left_line": 938, + "right_line": 947 + }, + { + "left_line": 939, + "right_line": 948 + }, + { + "left_line": 940, + "right_line": 949 + }, + { + "left_line": 941, + "right_line": 950 + }, + { + "left_line": 942, + "right_line": 951 + }, + { + "left_line": 943, + "right_line": 952 + }, + { + "left_line": 944, + "right_line": 953 + }, + { + "left_line": 945, + "right_line": 954 + }, + { + "left_line": 946, + "right_line": 955 + }, + { + "left_line": 947, + "right_line": 956 + }, + { + "left_line": 948, + "right_line": 957 + }, + { + "left_line": 949, + "right_line": 958 + }, + { + "left_line": 950, + "right_line": 959 + }, + { + "left_line": 951, + "right_line": 960 + }, + { + "left_line": 952, + "right_line": 961 + }, + { + "left_line": 953, + "right_line": 962 + }, + { + "left_line": 954, + "right_line": 963 + }, + { + "left_line": 955, + "right_line": 964 + }, + { + "left_line": 956, + "right_line": 965 + }, + { + "left_line": 957, + "right_line": 966 + }, + { + "left_line": 958, + "right_line": 967 + }, + { + "left_line": 959, + "right_line": 968 + }, + { + "left_line": 960, + "right_line": 969 + }, + { + "left_line": 961, + "right_line": 970 + }, + { + "left_line": 962, + "right_line": 971 + }, + { + "left_line": 963, + "right_line": 972 + }, + { + "left_line": 964, + "right_line": 973 + }, + { + "left_line": 965, + "right_line": 974 + }, + { + "left_line": 966, + "right_line": 975 + }, + { + "left_line": 967, + "right_line": 976 + }, + { + "left_line": 968, + "right_line": 977 + }, + { + "left_line": 969, + "right_line": 978 + }, + { + "left_line": 970, + "right_line": 979 + }, + { + "left_line": 971, + "right_line": 980 + }, + { + "left_line": 972, + "right_line": 981 + }, + { + "left_line": 973, + "right_line": 982 + }, + { + "left_line": 974, + "right_line": 983 + }, + { + "left_line": 975, + "right_line": 984 + }, + { + "left_line": 976, + "right_line": 985 + }, + { + "left_line": 977, + "right_line": 986 + }, + { + "left_line": 978, + "right_line": 987 + }, + { + "left_line": 979, + "right_line": 988 + }, + { + "left_line": 980, + "right_line": 989 + }, + { + "left_line": 981, + "right_line": 990 + }, + { + "left_line": 982, + "right_line": 991 + }, + { + "left_line": 983, + "right_line": 992 + }, + { + "left_line": 984, + "right_line": 993 + }, + { + "left_line": 985, + "right_line": 994 + }, + { + "left_line": 986, + "right_line": 995 + }, + { + "left_line": 987, + "right_line": 996 + }, + { + "left_line": 988, + "right_line": 997 + }, + { + "left_line": 989, + "right_line": 998 + }, + { + "left_line": 990, + "right_line": 999 + }, + { + "left_line": 991, + "right_line": 1000 + }, + { + "left_line": 992, + "right_line": 1001 + }, + { + "left_line": 993, + "right_line": 1002 + }, + { + "left_line": 994, + "right_line": 1003 + }, + { + "left_line": 995, + "right_line": 1004 + }, + { + "left_line": 996, + "right_line": 1005 + }, + { + "left_line": 997, + "right_line": 1006 + }, + { + "left_line": 998, + "right_line": 1007 + }, + { + "left_line": 999, + "right_line": 1008 + }, + { + "left_line": 1000, + "right_line": 1009 + }, + { + "left_line": 1001, + "right_line": 1010 + }, + { + "left_line": 1002, + "right_line": 1011 + }, + { + "left_line": 1003, + "right_line": 1012 + }, + { + "left_line": 1004, + "right_line": 1013 + }, + { + "left_line": 1005, + "right_line": 1014 + }, + { + "left_line": 1006, + "right_line": 1015 + }, + { + "left_line": 1007, + "right_line": 1016 + }, + { + "left_line": 1008, + "right_line": 1017 + }, + { + "left_line": 1009, + "right_line": 1018 + }, + { + "left_line": 1010, + "right_line": 1019 + }, + { + "left_line": 1011, + "right_line": 1020 + }, + { + "left_line": 1012, + "right_line": 1021 + }, + { + "left_line": 1013, + "right_line": 1022 + }, + { + "left_line": 1014, + "right_line": 1023 + }, + { + "left_line": 1015, + "right_line": 1024 + }, + { + "left_line": 1016, + "right_line": 1025 + }, + { + "left_line": 1017, + "right_line": 1026 + }, + { + "left_line": 1018, + "right_line": 1027 + }, + { + "left_line": 1019, + "right_line": 1028 + }, + { + "left_line": 1020, + "right_line": 1029 + }, + { + "left_line": 1021, + "right_line": 1030 + }, + { + "left_line": 1022, + "right_line": 1031 + }, + { + "left_line": 1023, + "right_line": 1032 + }, + { + "left_line": 1024, + "right_line": 1033 + }, + { + "left_line": 1025, + "right_line": 1034 + }, + { + "left_line": 1026, + "right_line": 1035 + }, + { + "left_line": 1027, + "right_line": 1036 + }, + { + "left_line": 1028, + "right_line": 1037 + }, + { + "left_line": 1029, + "right_line": 1038 + }, + { + "left_line": 1030, + "right_line": 1039 + }, + { + "left_line": 1031, + "right_line": 1040 + }, + { + "left_line": 1032, + "right_line": 1041 + }, + { + "left_line": 1033, + "right_line": 1042 + }, + { + "left_line": 1034, + "right_line": 1043 + }, + { + "left_line": 1035, + "right_line": 1044 + }, + { + "left_line": 1036, + "right_line": 1045 + }, + { + "left_line": 1037, + "right_line": 1046 + }, + { + "left_line": 1038, + "right_line": 1047 + }, + { + "left_line": 1039, + "right_line": 1048 + }, + { + "left_line": 1040, + "right_line": 1049 + }, + { + "left_line": 1041, + "right_line": 1050 + }, + { + "left_line": 1042, + "right_line": 1051 + }, + { + "left_line": 1043, + "right_line": 1052 + }, + { + "left_line": 1044, + "right_line": 1053 + }, + { + "left_line": 1045, + "right_line": 1054 + }, + { + "left_line": 1046, + "right_line": 1055 + }, + { + "left_line": 1047, + "right_line": 1056 + }, + { + "left_line": 1048, + "right_line": 1057 + }, + { + "left_line": 1049, + "right_line": 1058 + }, + { + "left_line": 1050, + "right_line": 1059 + }, + { + "left_line": 1051, + "right_line": 1060 + }, + { + "left_line": 1052, + "right_line": 1061 + }, + { + "left_line": 1053, + "right_line": 1062 + }, + { + "left_line": 1054, + "right_line": 1063 + }, + { + "left_line": 1055, + "right_line": 1064 + }, + { + "left_line": 1056, + "right_line": 1065 + }, + { + "left_line": 1057, + "right_line": 1066 + }, + { + "left_line": 1058, + "right_line": 1067 + }, + { + "left_line": 1059, + "right_line": 1068 + }, + { + "left_line": 1060, + "right_line": 1069 + }, + { + "left_line": 1061, + "right_line": 1070 + }, + { + "left_line": 1062, + "right_line": 1071 + }, + { + "left_line": 1063, + "right_line": 1072 + }, + { + "left_line": 1064, + "right_line": 1073 + }, + { + "left_line": 1065, + "right_line": 1074 + }, + { + "left_line": 1066, + "right_line": 1075 + }, + { + "left_line": 1067, + "right_line": 1076 + }, + { + "left_line": 1068, + "right_line": 1077 + }, + { + "left_line": 1069, + "right_line": 1078 + }, + { + "left_line": 1070, + "right_line": 1079 + }, + { + "left_line": 1071, + "right_line": 1080 + }, + { + "left_line": 1072, + "right_line": 1081 + }, + { + "left_line": 1073, + "right_line": 1082 + }, + { + "left_line": 1074, + "right_line": 1083 + }, + { + "left_line": 1075, + "right_line": 1084 + }, + { + "left_line": 1076, + "right_line": 1085 + }, + { + "left_line": 1077, + "right_line": 1086 + }, + { + "left_line": 1078, + "right_line": 1087 + }, + { + "left_line": 1079, + "right_line": 1088 + }, + { + "left_line": 1080, + "right_line": 1089 + }, + { + "left_line": 1081, + "right_line": 1090 + }, + { + "left_line": 1082, + "right_line": 1091 + }, + { + "left_line": 1083, + "right_line": 1092 + }, + { + "left_line": 1084, + "right_line": 1093 + }, + { + "left_line": 1085, + "right_line": 1094 + }, + { + "left_line": 1086, + "right_line": 1095 + }, + { + "left_line": 1087, + "right_line": 1096 + }, + { + "left_line": 1088, + "right_line": 1097 + }, + { + "left_line": 1089, + "right_line": 1098 + }, + { + "left_line": 1090, + "right_line": 1099 + }, + { + "left_line": 1091, + "right_line": 1100 + }, + { + "left_line": 1092, + "right_line": 1101 + }, + { + "left_line": 1093, + "right_line": 1102 + }, + { + "left_line": 1094, + "right_line": 1103 + }, + { + "left_line": 1095, + "right_line": 1104 + }, + { + "left_line": 1096, + "right_line": 1105 + }, + { + "left_line": 1097, + "right_line": 1106 + }, + { + "left_line": 1098, + "right_line": 1107 + }, + { + "left_line": 1099, + "right_line": 1108 + }, + { + "left_line": 1100, + "right_line": 1109 + }, + { + "left_line": 1101, + "right_line": 1110 + }, + { + "left_line": 1102, + "right_line": 1111 + }, + { + "left_line": 1103, + "right_line": 1112 + }, + { + "left_line": 1104, + "right_line": 1113 + }, + { + "left_line": 1105, + "right_line": 1114 + }, + { + "left_line": 1106, + "right_line": 1115 + }, + { + "left_line": 1107, + "right_line": 1116 + }, + { + "left_line": 1108, + "right_line": 1117 + }, + { + "left_line": 1109, + "right_line": 1118 + }, + { + "left_line": 1110, + "right_line": 1119 + }, + { + "left_line": 1111, + "right_line": 1120 + }, + { + "left_line": 1112, + "right_line": 1121 + }, + { + "left_line": 1113, + "right_line": 1122 + }, + { + "left_line": 1114, + "right_line": 1123 + }, + { + "left_line": 1115, + "right_line": 1124 + }, + { + "left_line": 1116, + "right_line": 1125 + }, + { + "left_line": 1117, + "right_line": 1126 + }, + { + "left_line": 1118, + "right_line": 1127 + }, + { + "left_line": 1119, + "right_line": 1128 + }, + { + "left_line": 1120, + "right_line": 1129 + }, + { + "left_line": 1121, + "right_line": 1130 + }, + { + "left_line": 1122, + "right_line": 1131 + }, + { + "left_line": 1123, + "right_line": 1132 + }, + { + "left_line": 1124, + "right_line": 1133 + }, + { + "left_line": 1125, + "right_line": 1134 + }, + { + "left_line": 1126, + "right_line": 1135 + }, + { + "left_line": 1127, + "right_line": 1136 + }, + { + "left_line": 1128, + "right_line": 1137 + }, + { + "left_line": 1129, + "right_line": 1138 + }, + { + "left_line": 1130, + "right_line": 1139 + }, + { + "left_line": 1131, + "right_line": 1140 + }, + { + "left_line": 1132, + "right_line": 1141 + }, + { + "left_line": 1133, + "right_line": 1142 + }, + { + "left_line": 1134, + "right_line": 1143 + }, + { + "left_line": 1135, + "right_line": 1144 + }, + { + "left_line": 1136, + "right_line": 1145 + }, + { + "left_line": 1137, + "right_line": 1146 + }, + { + "left_line": 1138, + "right_line": 1147 + }, + { + "left_line": 1139, + "right_line": 1148 + }, + { + "left_line": 1140, + "right_line": 1149 + }, + { + "left_line": 1141, + "right_line": 1150 + }, + { + "left_line": 1142, + "right_line": 1151 + }, + { + "left_line": 1143, + "right_line": 1152 + }, + { + "left_line": 1144, + "right_line": 1153 + }, + { + "left_line": 1145, + "right_line": 1154 + }, + { + "left_line": 1146, + "right_line": 1155 + }, + { + "left_line": 1147, + "right_line": 1156 + }, + { + "left_line": 1148, + "right_line": 1157 + }, + { + "left_line": 1149, + "right_line": 1158 + }, + { + "left_line": 1150, + "right_line": 1159 + }, + { + "left_line": 1151, + "right_line": 1160 + }, + { + "left_line": 1152, + "right_line": 1161 + }, + { + "left_line": 1153, + "right_line": 1162 + }, + { + "left_line": 1154, + "right_line": 1163 + }, + { + "left_line": 1155, + "right_line": 1164 + }, + { + "left_line": 1156, + "right_line": 1165 + }, + { + "left_line": 1157, + "right_line": 1166 + }, + { + "left_line": 1158, + "right_line": 1167 + }, + { + "left_line": 1159, + "right_line": 1168 + }, + { + "left_line": 1160, + "right_line": 1169 + }, + { + "left_line": 1161, + "right_line": 1170 + }, + { + "left_line": 1162, + "right_line": 1171 + }, + { + "left_line": 1163, + "right_line": 1172 + }, + { + "left_line": 1164, + "right_line": 1173 + }, + { + "left_line": 1165, + "right_line": 1174 + }, + { + "left_line": 1166, + "right_line": 1175 + }, + { + "left_line": 1167, + "right_line": 1176 + }, + { + "left_line": 1168, + "right_line": 1177 + }, + { + "left_line": 1169, + "right_line": 1178 + }, + { + "left_line": 1170, + "right_line": 1179 + }, + { + "left_line": 1171, + "right_line": 1180 + }, + { + "left_line": 1172, + "right_line": 1181 + }, + { + "left_line": 1173, + "right_line": 1182 + }, + { + "left_line": 1174, + "right_line": 1183 + }, + { + "left_line": 1175, + "right_line": 1184 + }, + { + "left_line": 1176, + "right_line": 1185 + }, + { + "left_line": 1177, + "right_line": 1186 + }, + { + "left_line": 1178, + "right_line": 1187 + }, + { + "left_line": 1179, + "right_line": 1188 + }, + { + "left_line": 1180, + "right_line": 1189 + }, + { + "left_line": 1181, + "right_line": 1190 + }, + { + "left_line": 1182, + "right_line": 1191 + }, + { + "left_line": 1183, + "right_line": 1192 + }, + { + "left_line": 1184, + "right_line": 1193 + }, + { + "left_line": 1185, + "right_line": 1194 + }, + { + "left_line": 1186, + "right_line": 1195 + }, + { + "left_line": 1187, + "right_line": 1196 + }, + { + "left_line": 1188, + "right_line": 1197 + }, + { + "left_line": 1189, + "right_line": 1198 + }, + { + "left_line": 1190, + "right_line": 1199 + }, + { + "left_line": 1191, + "right_line": 1200 + }, + { + "left_line": 1192, + "right_line": 1201 + }, + { + "left_line": 1193, + "right_line": 1202 + }, + { + "left_line": 1194, + "right_line": 1203 + }, + { + "left_line": 1195, + "right_line": 1204 + }, + { + "left_line": 1196, + "right_line": 1205 + }, + { + "left_line": 1197, + "right_line": 1206 + }, + { + "left_line": 1198, + "right_line": 1207 + }, + { + "left_line": 1199, + "right_line": 1208 + }, + { + "left_line": 1200, + "right_line": 1209 + }, + { + "left_line": 1201, + "right_line": 1210 + }, + { + "left_line": 1202, + "right_line": 1211 + }, + { + "left_line": 1203, + "right_line": 1212 + }, + { + "left_line": 1204, + "right_line": 1213 + }, + { + "left_line": 1205, + "right_line": 1214 + }, + { + "left_line": 1206, + "right_line": 1215 + }, + { + "left_line": 1207, + "right_line": 1216 + }, + { + "left_line": 1208, + "right_line": 1217 + }, + { + "left_line": 1209, + "right_line": 1218 + }, + { + "left_line": 1210, + "right_line": 1219 + }, + { + "left_line": 1211, + "right_line": 1220 + }, + { + "left_line": 1212, + "right_line": 1221 + }, + { + "left_line": 1213, + "right_line": 1222 + }, + { + "left_line": 1214, + "right_line": 1223 + }, + { + "left_line": 1215, + "right_line": 1224 + }, + { + "left_line": 1216, + "right_line": 1225 + }, + { + "left_line": 1217, + "right_line": 1226 + }, + { + "left_line": 1218, + "right_line": 1227 + }, + { + "left_line": 1219, + "right_line": 1228 + }, + { + "left_line": 1220, + "right_line": 1229 + }, + { + "left_line": 1221, + "right_line": 1230 + }, + { + "left_line": 1222, + "right_line": 1231 + }, + { + "left_line": 1223, + "right_line": 1232 + }, + { + "left_line": 1224, + "right_line": 1233 + }, + { + "left_line": 1225, + "right_line": 1234 + }, + { + "left_line": 1226, + "right_line": 1235 + }, + { + "left_line": 1227, + "right_line": 1236 + }, + { + "left_line": 1228, + "right_line": 1237 + }, + { + "left_line": 1229, + "right_line": 1238 + }, + { + "left_line": 1230, + "right_line": 1239 + }, + { + "left_line": 1231, + "right_line": 1240 + }, + { + "left_line": 1232, + "right_line": 1241 + }, + { + "left_line": 1233, + "right_line": 1242 + }, + { + "left_line": 1234, + "right_line": 1243 + }, + { + "left_line": 1235, + "right_line": 1244 + }, + { + "left_line": 1236, + "right_line": 1245 + }, + { + "left_line": 1237, + "right_line": 1246 + }, + { + "left_line": 1238, + "right_line": 1247 + }, + { + "left_line": 1239, + "right_line": 1248 + }, + { + "left_line": 1240, + "right_line": 1249 + }, + { + "left_line": 1241, + "right_line": 1250 + }, + { + "left_line": 1242, + "right_line": 1251 + }, + { + "left_line": 1243, + "right_line": 1252 + }, + { + "left_line": 1244, + "right_line": 1253 + }, + { + "left_line": 1245, + "right_line": 1254 + }, + { + "left_line": 1246, + "right_line": 1255 + }, + { + "left_line": 1247, + "right_line": 1256 + }, + { + "left_line": 1248, + "right_line": 1257 + }, + { + "left_line": 1249, + "right_line": 1258 + }, + { + "left_line": 1250, + "right_line": 1259 + }, + { + "left_line": 1251, + "right_line": 1260 + }, + { + "left_line": 1252, + "right_line": 1261 + }, + { + "left_line": 1253, + "right_line": 1262 + }, + { + "left_line": 1254, + "right_line": 1263 + }, + { + "left_line": 1255, + "right_line": 1264 + }, + { + "left_line": 1256, + "right_line": 1265 + }, + { + "left_line": 1257, + "right_line": 1266 + }, + { + "left_line": 1258, + "right_line": 1267 + }, + { + "left_line": 1259, + "right_line": 1268 + }, + { + "left_line": 1260, + "right_line": 1269 + }, + { + "left_line": 1261, + "right_line": 1270 + }, + { + "left_line": 1262, + "right_line": 1271 + }, + { + "left_line": 1263, + "right_line": 1272 + }, + { + "left_line": 1264, + "right_line": 1273 + }, + { + "left_line": 1265, + "right_line": 1274 + }, + { + "left_line": 1266, + "right_line": 1275 + }, + { + "left_line": 1267, + "right_line": 1276 + }, + { + "left_line": 1268, + "right_line": 1277 + }, + { + "left_line": 1269, + "right_line": 1278 + }, + { + "left_line": 1270, + "right_line": 1279 + }, + { + "left_line": 1271, + "right_line": 1280 + }, + { + "left_line": 1272, + "right_line": 1281 + }, + { + "left_line": 1273, + "right_line": 1282 + }, + { + "left_line": 1274, + "right_line": 1283 + }, + { + "left_line": 1275, + "right_line": 1284 + }, + { + "left_line": 1276, + "right_line": 1285 + }, + { + "left_line": 1277, + "right_line": 1286 + }, + { + "left_line": 1278, + "right_line": 1287 + }, + { + "left_line": 1279, + "right_line": 1288 + }, + { + "left_line": 1280, + "right_line": 1289 + }, + { + "left_line": 1281, + "right_line": 1290 + }, + { + "left_line": 1282, + "right_line": 1291 + }, + { + "left_line": 1283, + "right_line": 1292 + }, + { + "left_line": 1284, + "right_line": 1293 + }, + { + "left_line": 1285, + "right_line": 1294 + }, + { + "left_line": 1286, + "right_line": 1295 + }, + { + "left_line": 1287, + "right_line": 1296 + }, + { + "left_line": 1288, + "right_line": 1297 + }, + { + "left_line": 1289, + "right_line": 1298 + }, + { + "left_line": 1290, + "right_line": 1299 + }, + { + "left_line": 1291, + "right_line": 1300 + }, + { + "left_line": 1292, + "right_line": 1301 + }, + { + "left_line": 1293, + "right_line": 1302 + }, + { + "left_line": 1294, + "right_line": 1303 + }, + { + "left_line": 1295, + "right_line": 1304 + }, + { + "left_line": 1296, + "right_line": 1305 + }, + { + "left_line": 1297, + "right_line": 1306 + }, + { + "left_line": 1298, + "right_line": 1307 + }, + { + "left_line": 1299, + "right_line": 1308 + }, + { + "left_line": 1300, + "right_line": 1309 + }, + { + "left_line": 1301, + "right_line": 1310 + }, + { + "left_line": 1302, + "right_line": 1311 + }, + { + "left_line": 1303, + "right_line": 1312 + }, + { + "left_line": 1304, + "right_line": 1313 + }, + { + "left_line": 1305, + "right_line": 1314 + }, + { + "left_line": 1306, + "right_line": 1315 + }, + { + "left_line": 1307, + "right_line": 1316 + }, + { + "left_line": 1308, + "right_line": 1317 + }, + { + "left_line": 1309, + "right_line": 1318 + }, + { + "left_line": 1310, + "right_line": 1319 + }, + { + "left_line": 1311, + "right_line": 1320 + }, + { + "left_line": 1312, + "right_line": 1321 + }, + { + "left_line": 1313, + "right_line": 1322 + }, + { + "left_line": 1314, + "right_line": 1323 + }, + { + "left_line": 1315, + "right_line": 1324 + }, + { + "left_line": 1316, + "right_line": 1325 + }, + { + "left_line": 1317, + "right_line": 1326 + }, + { + "left_line": 1318, + "right_line": 1327 + }, + { + "left_line": 1319, + "right_line": 1328 + }, + { + "left_line": 1320, + "right_line": 1329 + }, + { + "left_line": 1321, + "right_line": 1330 + }, + { + "left_line": 1322, + "right_line": 1331 + }, + { + "left_line": 1323, + "right_line": 1332 + }, + { + "left_line": 1324, + "right_line": 1333 + }, + { + "left_line": 1325, + "right_line": 1334 + }, + { + "left_line": 1326, + "right_line": 1335 + }, + { + "left_line": 1327, + "right_line": 1336 + }, + { + "left_line": 1328, + "right_line": 1337 + }, + { + "left_line": 1329, + "right_line": 1338 + }, + { + "left_line": 1330, + "right_line": 1339 + }, + { + "left_line": 1331, + "right_line": 1340 + }, + { + "left_line": 1332, + "right_line": 1341 + }, + { + "left_line": 1333, + "right_line": 1342 + }, + { + "left_line": 1334, + "right_line": 1343 + }, + { + "left_line": 1335, + "right_line": 1344 + }, + { + "left_line": 1336, + "right_line": 1345 + }, + { + "left_line": 1337, + "right_line": 1346 + }, + { + "left_line": 1338, + "right_line": 1347 + }, + { + "left_line": 1339, + "right_line": 1348 + }, + { + "left_line": 1340, + "right_line": 1349 + }, + { + "left_line": 1341, + "right_line": 1350 + }, + { + "left_line": 1342, + "right_line": 1351 + }, + { + "left_line": 1343, + "right_line": 1352 + }, + { + "left_line": 1344, + "right_line": 1353 + }, + { + "left_line": 1345, + "right_line": 1354 + }, + { + "left_line": 1346, + "right_line": 1355 + }, + { + "left_line": 1347, + "right_line": 1356 + }, + { + "left_line": 1348, + "right_line": 1357 + }, + { + "left_line": 1349, + "right_line": 1358 + }, + { + "left_line": 1350, + "right_line": 1359 + }, + { + "left_line": 1351, + "right_line": 1360 + }, + { + "left_line": 1352, + "right_line": 1361 + }, + { + "left_line": 1353, + "right_line": 1362 + }, + { + "left_line": 1354, + "right_line": 1363 + }, + { + "left_line": 1355, + "right_line": 1364 + }, + { + "left_line": 1356, + "right_line": 1365 + }, + { + "left_line": 1357, + "right_line": 1366 + }, + { + "left_line": 1358, + "right_line": 1367 + }, + { + "left_line": 1359, + "right_line": 1368 + }, + { + "left_line": 1360, + "right_line": 1369 + }, + { + "left_line": 1361, + "right_line": 1370 + }, + { + "left_line": 1362, + "right_line": 1371 + }, + { + "left_line": 1363, + "right_line": 1372 + }, + { + "left_line": 1364, + "right_line": 1373 + }, + { + "left_line": 1365, + "right_line": 1374 + }, + { + "left_line": 1366, + "right_line": 1375 + }, + { + "left_line": 1367, + "right_line": 1376 + }, + { + "left_line": 1368, + "right_line": 1377 + }, + { + "left_line": 1369, + "right_line": 1378 + }, + { + "left_line": 1370, + "right_line": 1379 + }, + { + "left_line": 1371, + "right_line": 1380 + }, + { + "left_line": 1372, + "right_line": 1381 + }, + { + "left_line": 1373, + "right_line": 1382 + }, + { + "left_line": 1374, + "right_line": 1383 + }, + { + "left_line": 1375, + "right_line": 1384 + }, + { + "left_line": 1376, + "right_line": 1385 + }, + { + "left_line": 1377, + "right_line": 1386 + }, + { + "left_line": 1378, + "right_line": 1387 + }, + { + "left_line": 1379, + "right_line": 1388 + }, + { + "left_line": 1380, + "right_line": 1389 + }, + { + "left_line": 1381, + "right_line": 1390 + }, + { + "left_line": 1382, + "right_line": 1391 + }, + { + "left_line": 1383, + "right_line": 1392 + }, + { + "left_line": 1384, + "right_line": 1393 + }, + { + "left_line": 1385, + "right_line": 1394 + }, + { + "left_line": 1386, + "right_line": 1395 + }, + { + "left_line": 1387, + "right_line": 1396 + }, + { + "left_line": 1388, + "right_line": 1397 + }, + { + "left_line": 1389, + "right_line": 1398 + }, + { + "left_line": 1390, + "right_line": 1399 + }, + { + "left_line": 1391, + "right_line": 1400 + }, + { + "left_line": 1392, + "right_line": 1401 + }, + { + "left_line": 1393, + "right_line": 1402 + }, + { + "left_line": 1394, + "right_line": 1403 + }, + { + "left_line": 1395, + "right_line": 1404 + }, + { + "left_line": 1396, + "right_line": 1405 + }, + { + "left_line": 1397, + "right_line": 1406 + }, + { + "left_line": 1398, + "right_line": 1407 + }, + { + "left_line": 1399, + "right_line": 1408 + }, + { + "left_line": 1400, + "right_line": 1409 + }, + { + "left_line": 1401, + "right_line": 1410 + }, + { + "left_line": 1402, + "right_line": 1411 + }, + { + "left_line": 1403, + "right_line": 1412 + }, + { + "left_line": 1404, + "right_line": 1413 + }, + { + "left_line": 1405, + "right_line": 1414 + }, + { + "left_line": 1406, + "right_line": 1415 + }, + { + "left_line": 1407, + "right_line": 1416 + }, + { + "left_line": 1408, + "right_line": 1417 + }, + { + "left_line": 1409, + "right_line": 1418 + }, + { + "left_line": 1410, + "right_line": 1419 + }, + { + "left_line": 1411, + "right_line": 1420 + }, + { + "left_line": 1412, + "right_line": 1421 + }, + { + "left_line": 1413, + "right_line": 1422 + }, + { + "left_line": 1414, + "right_line": 1423 + }, + { + "left_line": 1415, + "right_line": 1424 + }, + { + "left_line": 1416, + "right_line": 1425 + }, + { + "left_line": 1417, + "right_line": 1426 + }, + { + "left_line": 1418, + "right_line": 1427 + }, + { + "left_line": 1419, + "right_line": 1428 + }, + { + "left_line": 1420, + "right_line": 1429 + }, + { + "left_line": 1421, + "right_line": 1430 + }, + { + "left_line": 1422, + "right_line": 1431 + }, + { + "left_line": 1423, + "right_line": 1432 + }, + { + "left_line": 1424, + "right_line": 1433 + }, + { + "left_line": 1425, + "right_line": 1434 + }, + { + "left_line": 1426, + "right_line": 1435 + }, + { + "left_line": 1427, + "right_line": 1436 + }, + { + "left_line": 1428, + "right_line": 1437 + }, + { + "left_line": 1429, + "right_line": 1438 + }, + { + "left_line": 1430, + "right_line": 1439 + }, + { + "left_line": 1431, + "right_line": 1440 + }, + { + "left_line": 1432, + "right_line": 1441 + }, + { + "left_line": 1433, + "right_line": 1442 + }, + { + "left_line": 1434, + "right_line": 1443 + }, + { + "left_line": 1435, + "right_line": 1444 + }, + { + "left_line": 1436, + "right_line": 1445 + }, + { + "left_line": 1437, + "right_line": 1446 + }, + { + "left_line": 1438, + "right_line": 1447 + }, + { + "left_line": 1439, + "right_line": 1448 + }, + { + "left_line": 1440, + "right_line": 1449 + }, + { + "left_line": 1441, + "right_line": 1450 + }, + { + "left_line": 1442, + "right_line": 1451 + }, + { + "left_line": 1443, + "right_line": 1452 + }, + { + "left_line": 1444, + "right_line": 1453 + }, + { + "left_line": 1445, + "right_line": 1454 + }, + { + "left_line": 1446, + "right_line": 1455 + }, + { + "left_line": 1447, + "right_line": 1456 + }, + { + "left_line": 1448, + "right_line": 1457 + }, + { + "left_line": 1449, + "right_line": 1458 + }, + { + "left_line": 1450, + "right_line": 1459 + }, + { + "left_line": 1451, + "right_line": 1460 + }, + { + "left_line": 1452, + "right_line": 1461 + }, + { + "left_line": 1453, + "right_line": 1462 + }, + { + "left_line": 1454, + "right_line": 1463 + }, + { + "left_line": 1455, + "right_line": 1464 + }, + { + "left_line": 1456, + "right_line": 1465 + }, + { + "left_line": 1457, + "right_line": 1466 + }, + { + "left_line": 1458, + "right_line": 1467 + }, + { + "left_line": 1459, + "right_line": 1468 + }, + { + "left_line": 1460, + "right_line": 1469 + }, + { + "left_line": 1461, + "right_line": 1470 + }, + { + "left_line": 1462, + "right_line": 1471 + }, + { + "left_line": 1463, + "right_line": 1472 + }, + { + "left_line": 1464, + "right_line": 1473 + }, + { + "left_line": 1465, + "right_line": 1474 + }, + { + "left_line": 1466, + "right_line": 1475 + }, + { + "left_line": 1467, + "right_line": 1476 + }, + { + "left_line": 1468, + "right_line": 1477 + }, + { + "left_line": 1469, + "right_line": 1478 + }, + { + "left_line": 1470, + "right_line": 1479 + }, + { + "left_line": 1471, + "right_line": 1480 + }, + { + "left_line": 1472, + "right_line": 1481 + }, + { + "left_line": 1473, + "right_line": 1482 + }, + { + "left_line": 1474, + "right_line": 1483 + }, + { + "left_line": 1475, + "right_line": 1484 + }, + { + "left_line": 1476, + "right_line": 1485 + }, + { + "left_line": 1477, + "right_line": 1486 + }, + { + "left_line": 1478, + "right_line": 1487 + }, + { + "left_line": 1479, + "right_line": 1488 + }, + { + "left_line": 1480, + "right_line": 1489 + }, + { + "left_line": 1481, + "right_line": 1490 + }, + { + "left_line": 1482, + "right_line": 1491 + }, + { + "left_line": 1483, + "right_line": 1492 + }, + { + "left_line": 1484, + "right_line": 1493 + }, + { + "left_line": 1485, + "right_line": 1494 + }, + { + "left_line": 1486, + "right_line": 1495 + }, + { + "left_line": 1487, + "right_line": 1496 + }, + { + "left_line": 1488, + "right_line": 1497 + }, + { + "left_line": 1489, + "right_line": 1498 + }, + { + "left_line": 1490, + "right_line": 1499 + }, + { + "left_line": 1491, + "right_line": 1500 + }, + { + "left_line": 1492, + "right_line": 1501 + }, + { + "left_line": 1493, + "right_line": 1502 + }, + { + "left_line": 1494, + "right_line": 1503 + }, + { + "left_line": 1495, + "right_line": 1504 + }, + { + "left_line": 1496, + "right_line": 1505 + }, + { + "left_line": 1497, + "right_line": 1506 + }, + { + "left_line": 1498, + "right_line": 1507 + }, + { + "left_line": 1499, + "right_line": 1508 + }, + { + "left_line": 1500, + "right_line": 1509 + }, + { + "left_line": 1501, + "right_line": 1510 + }, + { + "left_line": 1502, + "right_line": 1511 + }, + { + "left_line": 1503, + "right_line": 1512 + }, + { + "left_line": 1504, + "right_line": 1513 + }, + { + "left_line": 1505, + "right_line": 1514 + }, + { + "left_line": 1506, + "right_line": 1515 + }, + { + "left_line": 1507, + "right_line": 1516 + }, + { + "left_line": 1508, + "right_line": 1517 + }, + { + "left_line": 1509, + "right_line": 1518 + }, + { + "left_line": 1510, + "right_line": 1519 + }, + { + "left_line": 1511, + "right_line": 1520 + }, + { + "left_line": 1512, + "right_line": 1521 + }, + { + "left_line": 1513, + "right_line": 1522 + }, + { + "left_line": 1514, + "right_line": 1523 + }, + { + "left_line": 1515, + "right_line": 1524 + }, + { + "left_line": 1516, + "right_line": 1525 + }, + { + "left_line": 1517, + "right_line": 1526 + }, + { + "left_line": 1518, + "right_line": 1527 + }, + { + "left_line": 1519, + "right_line": 1528 + }, + { + "left_line": 1520, + "right_line": 1529 + }, + { + "left_line": 1521, + "right_line": 1530 + }, + { + "left_line": 1522, + "right_line": 1531 + }, + { + "left_line": 1523, + "right_line": 1532 + }, + { + "left_line": 1524, + "right_line": 1533 + }, + { + "left_line": 1525, + "right_line": 1534 + }, + { + "left_line": 1526, + "right_line": 1535 + }, + { + "left_line": 1527, + "right_line": 1536 + }, + { + "left_line": 1528, + "right_line": 1537 + }, + { + "left_line": 1529, + "right_line": 1538 + }, + { + "left_line": 1530, + "right_line": 1539 + }, + { + "left_line": 1531, + "right_line": 1540 + }, + { + "left_line": 1532, + "right_line": 1541 + }, + { + "left_line": 1533, + "right_line": 1542 + }, + { + "left_line": 1534, + "right_line": 1543 + }, + { + "left_line": 1535, + "right_line": 1544 + }, + { + "left_line": 1536, + "right_line": 1545 + }, + { + "left_line": 1537, + "right_line": 1546 + }, + { + "left_line": 1538, + "right_line": 1547 + }, + { + "left_line": 1539, + "right_line": 1548 + }, + { + "left_line": 1540, + "right_line": 1549 + }, + { + "left_line": 1541, + "right_line": 1550 + }, + { + "left_line": 1542, + "right_line": 1551 + }, + { + "left_line": 1543, + "right_line": 1552 + }, + { + "left_line": 1544, + "right_line": 1553 + }, + { + "left_line": 1545, + "right_line": 1554 + }, + { + "left_line": 1546, + "right_line": 1555 + }, + { + "left_line": 1547, + "right_line": 1556 + }, + { + "left_line": 1548, + "right_line": 1557 + }, + { + "left_line": 1549, + "right_line": 1558 + }, + { + "left_line": 1550, + "right_line": 1559 + }, + { + "left_line": 1551, + "right_line": 1560 + }, + { + "left_line": 1552, + "right_line": 1561 + }, + { + "left_line": 1553, + "right_line": 1562 + }, + { + "left_line": 1554, + "right_line": 1563 + }, + { + "left_line": 1555, + "right_line": 1564 + }, + { + "left_line": 1556, + "right_line": 1565 + }, + { + "left_line": 1557, + "right_line": 1566 + }, + { + "left_line": 1558, + "right_line": 1567 + }, + { + "left_line": 1559, + "right_line": 1568 + }, + { + "left_line": 1560, + "right_line": 1569 + }, + { + "left_line": 1561, + "right_line": 1570 + }, + { + "left_line": 1562, + "right_line": 1571 + }, + { + "left_line": 1563, + "right_line": 1572 + }, + { + "left_line": 1564, + "right_line": 1573 + }, + { + "left_line": 1565, + "right_line": 1574 + }, + { + "left_line": 1566, + "right_line": 1575 + }, + { + "left_line": 1567, + "right_line": 1576 + }, + { + "left_line": 1568, + "right_line": 1577 + }, + { + "left_line": 1569, + "right_line": 1578 + }, + { + "left_line": 1570, + "right_line": 1579 + }, + { + "left_line": 1571, + "right_line": 1580 + }, + { + "left_line": 1572, + "right_line": 1581 + }, + { + "left_line": 1573, + "right_line": 1582 + }, + { + "left_line": 1574, + "right_line": 1583 + }, + { + "left_line": 1575, + "right_line": 1584 + }, + { + "left_line": 1576, + "right_line": 1585 + }, + { + "left_line": 1577, + "right_line": 1586 + }, + { + "left_line": 1578, + "right_line": 1587 + }, + { + "left_line": 1579, + "right_line": 1588 + }, + { + "left_line": 1580, + "right_line": 1589 + }, + { + "left_line": 1581, + "right_line": 1590 + }, + { + "left_line": 1582, + "right_line": 1591 + }, + { + "left_line": 1583, + "right_line": 1592 + }, + { + "left_line": 1584, + "right_line": 1593 + }, + { + "left_line": 1585, + "right_line": 1594 + }, + { + "left_line": 1586, + "right_line": 1595 + }, + { + "left_line": 1587, + "right_line": 1596 + }, + { + "left_line": 1588, + "right_line": 1597 + }, + { + "left_line": 1589, + "right_line": 1598 + }, + { + "left_line": 1590, + "right_line": 1599 + }, + { + "left_line": 1591, + "right_line": 1600 + }, + { + "left_line": 1592, + "right_line": 1601 + }, + { + "left_line": 1593, + "right_line": 1602 + }, + { + "left_line": 1594, + "right_line": 1603 + }, + { + "left_line": 1595, + "right_line": 1604 + }, + { + "left_line": 1596, + "right_line": 1605 + }, + { + "left_line": 1597, + "right_line": 1606 + }, + { + "left_line": 1598, + "right_line": 1607 + }, + { + "left_line": 1599, + "right_line": 1608 + }, + { + "left_line": 1600, + "right_line": 1609 + }, + { + "left_line": 1601, + "right_line": 1610 + }, + { + "left_line": 1602, + "right_line": 1611 + }, + { + "left_line": 1603, + "right_line": 1612 + }, + { + "left_line": 1604, + "right_line": 1613 + }, + { + "left_line": 1605, + "right_line": 1614 + }, + { + "left_line": 1606, + "right_line": 1615 + }, + { + "left_line": 1607, + "right_line": 1616 + }, + { + "left_line": 1608, + "right_line": 1617 + }, + { + "left_line": 1609, + "right_line": 1618 + }, + { + "left_line": 1610, + "right_line": 1619 + }, + { + "left_line": 1611, + "right_line": 1620 + }, + { + "left_line": 1612, + "right_line": 1621 + }, + { + "left_line": 1613, + "right_line": 1622 + }, + { + "left_line": 1614, + "right_line": 1623 + }, + { + "left_line": 1615, + "right_line": 1624 + }, + { + "left_line": 1616, + "right_line": 1625 + }, + { + "left_line": 1617, + "right_line": 1626 + }, + { + "left_line": 1618, + "right_line": 1627 + }, + { + "left_line": 1619, + "right_line": 1628 + }, + { + "left_line": 1620, + "right_line": 1629 + }, + { + "left_line": 1621, + "right_line": 1630 + }, + { + "left_line": 1622, + "right_line": 1631 + }, + { + "left_line": 1623, + "right_line": 1632 + }, + { + "left_line": 1624, + "right_line": 1633 + }, + { + "left_line": 1625, + "right_line": 1634 + }, + { + "left_line": 1626, + "right_line": 1635 + }, + { + "left_line": 1627, + "right_line": 1636 + }, + { + "left_line": 1628, + "right_line": 1637 + }, + { + "left_line": 1629, + "right_line": 1638 + }, + { + "left_line": 1630, + "right_line": 1639 + }, + { + "left_line": 1631, + "right_line": 1640 + }, + { + "left_line": 1632, + "right_line": 1641 + }, + { + "left_line": 1633, + "right_line": 1642 + }, + { + "left_line": 1634, + "right_line": 1643 + }, + { + "left_line": 1635, + "right_line": 1644 + }, + { + "left_line": 1636, + "right_line": 1645 + }, + { + "left_line": 1637, + "right_line": 1646 + }, + { + "left_line": 1638, + "right_line": 1647 + }, + { + "left_line": 1639, + "right_line": 1648 + }, + { + "left_line": 1640, + "right_line": 1649 + }, + { + "left_line": 1641, + "right_line": 1650 + }, + { + "left_line": 1642, + "right_line": 1651 + }, + { + "left_line": 1643, + "right_line": 1652 + }, + { + "left_line": 1644, + "right_line": 1653 + }, + { + "left_line": 1645, + "right_line": 1654 + }, + { + "left_line": 1646, + "right_line": 1655 + }, + { + "left_line": 1647, + "right_line": 1656 + }, + { + "left_line": 1648, + "right_line": 1657 + }, + { + "left_line": 1649, + "right_line": 1658 + }, + { + "left_line": 1650, + "right_line": 1659 + }, + { + "left_line": 1651, + "right_line": 1660 + }, + { + "left_line": 1652, + "right_line": 1661 + }, + { + "left_line": 1653, + "right_line": 1662 + }, + { + "left_line": 1654, + "right_line": 1663 + }, + { + "left_line": 1655, + "right_line": 1664 + }, + { + "left_line": 1656, + "right_line": 1665 + }, + { + "left_line": 1657, + "right_line": 1666 + }, + { + "left_line": 1658, + "right_line": 1667 + }, + { + "left_line": 1659, + "right_line": 1668 + }, + { + "left_line": 1660, + "right_line": 1669 + }, + { + "left_line": 1661, + "right_line": 1670 + }, + { + "left_line": 1662, + "right_line": 1671 + }, + { + "left_line": 1663, + "right_line": 1672 + }, + { + "left_line": 1664, + "right_line": 1673 + }, + { + "left_line": 1665, + "right_line": 1674 + }, + { + "left_line": 1666, + "right_line": 1675 + }, + { + "left_line": 1667, + "right_line": 1676 + }, + { + "left_line": 1668, + "right_line": 1677 + }, + { + "left_line": 1669, + "right_line": 1678 + }, + { + "left_line": 1670, + "right_line": 1679 + }, + { + "left_line": 1671, + "right_line": 1680 + }, + { + "left_line": 1672, + "right_line": 1681 + }, + { + "left_line": 1673, + "right_line": 1682 + }, + { + "left_line": 1674, + "right_line": 1683 + }, + { + "left_line": 1675, + "right_line": 1684 + }, + { + "left_line": 1676, + "right_line": 1685 + }, + { + "left_line": 1677, + "right_line": 1686 + }, + { + "left_line": 1678, + "right_line": 1687 + }, + { + "left_line": 1679, + "right_line": 1688 + }, + { + "left_line": 1680, + "right_line": 1689 + }, + { + "left_line": 1681, + "right_line": 1690 + }, + { + "left_line": 1682, + "right_line": 1691 + }, + { + "left_line": 1683, + "right_line": 1692 + }, + { + "left_line": 1684, + "right_line": 1693 + }, + { + "left_line": 1685, + "right_line": 1694 + }, + { + "left_line": 1686, + "right_line": 1695 + }, + { + "left_line": 1687, + "right_line": 1696 + }, + { + "left_line": 1688, + "right_line": 1697 + }, + { + "left_line": 1689, + "right_line": 1698 + }, + { + "left_line": 1690, + "right_line": 1699 + }, + { + "left_line": 1691, + "right_line": 1700 + }, + { + "left_line": 1692, + "right_line": 1701 + }, + { + "left_line": 1693, + "right_line": 1702 + }, + { + "left_line": 1694, + "right_line": 1703 + }, + { + "left_line": 1695, + "right_line": 1704 + }, + { + "left_line": 1696, + "right_line": 1705 + }, + { + "left_line": 1697, + "right_line": 1706 + }, + { + "left_line": 1698, + "right_line": 1707 + }, + { + "left_line": 1699, + "right_line": 1708 + }, + { + "left_line": 1700, + "right_line": 1709 + }, + { + "left_line": 1701, + "right_line": 1710 + }, + { + "left_line": 1702, + "right_line": 1711 + }, + { + "left_line": 1703, + "right_line": 1712 + }, + { + "left_line": 1704, + "right_line": 1713 + }, + { + "left_line": 1705, + "right_line": 1714 + }, + { + "left_line": 1706, + "right_line": 1715 + }, + { + "left_line": 1707, + "right_line": 1716 + }, + { + "left_line": 1708, + "right_line": 1717 + }, + { + "left_line": 1709, + "right_line": 1718 + }, + { + "left_line": 1710, + "right_line": 1719 + }, + { + "left_line": 1711, + "right_line": 1720 + }, + { + "left_line": 1712, + "right_line": 1721 + }, + { + "left_line": 1713, + "right_line": 1722 + }, + { + "left_line": 1714, + "right_line": 1723 + }, + { + "left_line": 1715, + "right_line": 1724 + }, + { + "left_line": 1716, + "right_line": 1725 + }, + { + "left_line": 1717, + "right_line": 1726 + }, + { + "left_line": 1718, + "right_line": 1727 + }, + { + "left_line": 1719, + "right_line": 1728 + }, + { + "left_line": 1720, + "right_line": 1729 + }, + { + "left_line": 1721, + "right_line": 1730 + }, + { + "left_line": 1722, + "right_line": 1731 + }, + { + "left_line": 1723, + "right_line": 1732 + }, + { + "left_line": 1724, + "right_line": 1733 + }, + { + "left_line": 1725, + "right_line": 1734 + }, + { + "left_line": 1726, + "right_line": 1735 + }, + { + "left_line": 1727, + "right_line": 1736 + }, + { + "left_line": 1728, + "right_line": 1737 + }, + { + "left_line": 1729, + "right_line": 1738 + }, + { + "left_line": 1730, + "right_line": 1739 + }, + { + "left_line": 1731, + "right_line": 1740 + }, + { + "left_line": 1732, + "right_line": 1741 + }, + { + "left_line": 1733, + "right_line": 1742 + }, + { + "left_line": 1734, + "right_line": 1743 + }, + { + "left_line": 1735, + "right_line": 1744 + }, + { + "left_line": 1736, + "right_line": 1745 + }, + { + "left_line": 1737, + "right_line": 1746 + }, + { + "left_line": 1738, + "right_line": 1747 + }, + { + "left_line": 1739, + "right_line": 1748 + }, + { + "left_line": 1740, + "right_line": 1749 + }, + { + "left_line": 1741, + "right_line": 1750 + }, + { + "left_line": 1742, + "right_line": 1751 + }, + { + "left_line": 1743, + "right_line": 1752 + }, + { + "left_line": 1744, + "right_line": 1753 + }, + { + "left_line": 1745, + "right_line": 1754 + }, + { + "left_line": 1746, + "right_line": 1755 + }, + { + "left_line": 1747, + "right_line": 1756 + }, + { + "left_line": 1748, + "right_line": 1757 + }, + { + "left_line": 1749, + "right_line": 1758 + }, + { + "left_line": 1750, + "right_line": 1759 + }, + { + "left_line": 1751, + "right_line": 1760 + }, + { + "left_line": 1752, + "right_line": 1761 + }, + { + "left_line": 1753, + "right_line": 1762 + }, + { + "left_line": 1754, + "right_line": 1763 + }, + { + "left_line": 1755, + "right_line": 1764 + }, + { + "left_line": 1756, + "right_line": 1765 + }, + { + "left_line": 1757, + "right_line": 1766 + }, + { + "left_line": 1758, + "right_line": 1767 + }, + { + "left_line": 1759, + "right_line": 1768 + }, + { + "left_line": 1760, + "right_line": 1769 + }, + { + "left_line": 1761, + "right_line": 1770 + }, + { + "left_line": 1762, + "right_line": 1771 + }, + { + "left_line": 1763, + "right_line": 1772 + }, + { + "left_line": 1764, + "right_line": 1773 + }, + { + "left_line": 1765, + "right_line": 1774 + }, + { + "left_line": 1766, + "right_line": 1775 + }, + { + "left_line": 1767, + "right_line": 1776 + }, + { + "left_line": 1768, + "right_line": 1777 + }, + { + "left_line": 1769, + "right_line": 1778 + }, + { + "left_line": 1770, + "right_line": 1779 + }, + { + "left_line": 1771, + "right_line": 1780 + }, + { + "left_line": 1772, + "right_line": 1781 + }, + { + "left_line": 1773, + "right_line": 1782 + }, + { + "left_line": 1774, + "right_line": 1783 + }, + { + "left_line": 1775, + "right_line": 1784 + }, + { + "left_line": 1776, + "right_line": 1785 + }, + { + "left_line": 1777, + "right_line": 1786 + }, + { + "left_line": 1778, + "right_line": 1787 + }, + { + "left_line": 1779, + "right_line": 1788 + }, + { + "left_line": 1780, + "right_line": 1789 + }, + { + "left_line": 1781, + "right_line": 1790 + }, + { + "left_line": 1782, + "right_line": 1791 + }, + { + "left_line": 1783, + "right_line": 1792 + }, + { + "left_line": 1784, + "right_line": 1793 + }, + { + "left_line": 1785, + "right_line": 1794 + }, + { + "left_line": 1786, + "right_line": 1795 + }, + { + "left_line": 1787, + "right_line": 1796 + }, + { + "left_line": 1788, + "right_line": 1797 + }, + { + "left_line": 1789, + "right_line": 1798 + }, + { + "left_line": 1790, + "right_line": 1799 + }, + { + "left_line": 1791, + "right_line": 1800 + }, + { + "left_line": 1792, + "right_line": 1801 + }, + { + "left_line": 1793, + "right_line": 1802 + }, + { + "left_line": 1794, + "right_line": 1803 + }, + { + "left_line": 1795, + "right_line": 1804 + }, + { + "left_line": 1796, + "right_line": 1805 + }, + { + "left_line": 1797, + "right_line": 1806 + }, + { + "left_line": 1798, + "right_line": 1807 + }, + { + "left_line": 1799, + "right_line": 1808 + }, + { + "left_line": 1800, + "right_line": 1809 + }, + { + "left_line": 1801, + "right_line": 1810 + }, + { + "left_line": 1802, + "right_line": 1811 + }, + { + "left_line": 1803, + "right_line": 1812 + }, + { + "left_line": 1804, + "right_line": 1813 + }, + { + "left_line": 1805, + "right_line": 1814 + }, + { + "left_line": 1806, + "right_line": 1815 + }, + { + "left_line": 1807, + "right_line": 1816 + }, + { + "left_line": 1808, + "right_line": 1817 + }, + { + "left_line": 1809, + "right_line": 1818 + }, + { + "left_line": 1810, + "right_line": 1819 + }, + { + "left_line": 1811, + "right_line": 1820 + }, + { + "left_line": 1812, + "right_line": 1821 + }, + { + "left_line": 1813, + "right_line": 1822 + }, + { + "left_line": 1814, + "right_line": 1823 + }, + { + "left_line": 1815, + "right_line": 1824 + }, + { + "left_line": 1816, + "right_line": 1825 + }, + { + "left_line": 1817, + "right_line": 1826 + }, + { + "left_line": 1818, + "right_line": 1827 + }, + { + "left_line": 1819, + "right_line": 1828 + }, + { + "left_line": 1820, + "right_line": 1829 + }, + { + "left_line": 1821, + "right_line": 1830 + }, + { + "left_line": 1822, + "right_line": 1831 + }, + { + "left_line": 1823, + "right_line": 1832 + }, + { + "left_line": 1824, + "right_line": 1833 + }, + { + "left_line": 1825, + "right_line": 1834 + }, + { + "left_line": 1826, + "right_line": 1835 + }, + { + "left_line": 1827, + "right_line": 1836 + }, + { + "left_line": 1828, + "right_line": 1837 + }, + { + "left_line": 1829, + "right_line": 1838 + }, + { + "left_line": 1830, + "right_line": 1839 + }, + { + "left_line": 1831, + "right_line": 1840 + }, + { + "left_line": 1832, + "right_line": 1841 + }, + { + "left_line": 1833, + "right_line": 1842 + }, + { + "left_line": 1834, + "right_line": 1843 + }, + { + "left_line": 1835, + "right_line": 1844 + }, + { + "left_line": 1836, + "right_line": 1845 + }, + { + "left_line": 1837, + "right_line": 1846 + }, + { + "left_line": 1838, + "right_line": 1847 + }, + { + "left_line": 1839, + "right_line": 1848 + }, + { + "left_line": 1840, + "right_line": 1849 + }, + { + "left_line": 1841, + "right_line": 1850 + }, + { + "left_line": 1842, + "right_line": 1851 + }, + { + "left_line": 1843, + "right_line": 1852 + }, + { + "left_line": 1844, + "right_line": 1853 + }, + { + "left_line": 1845, + "right_line": 1854 + }, + { + "left_line": 1846, + "right_line": 1855 + }, + { + "left_line": 1847, + "right_line": 1856 + }, + { + "left_line": 1848, + "right_line": 1857 + }, + { + "left_line": 1849, + "right_line": 1858 + }, + { + "left_line": 1850, + "right_line": 1859 + }, + { + "left_line": 1851, + "right_line": 1860 + }, + { + "left_line": 1852, + "right_line": 1861 + }, + { + "left_line": 1853, + "right_line": 1862 + }, + { + "left_line": 1854, + "right_line": 1863 + }, + { + "left_line": 1855, + "right_line": 1864 + }, + { + "left_line": 1856, + "right_line": 1865 + }, + { + "left_line": 1857, + "right_line": 1866 + }, + { + "left_line": 1858, + "right_line": 1867 + }, + { + "left_line": 1859, + "right_line": 1868 + }, + { + "left_line": 1860, + "right_line": 1869 + }, + { + "left_line": 1861, + "right_line": 1870 + }, + { + "left_line": 1862, + "right_line": 1871 + }, + { + "left_line": 1863, + "right_line": 1872 + }, + { + "left_line": 1864, + "right_line": 1873 + }, + { + "left_line": 1865, + "right_line": 1874 + }, + { + "left_line": 1866, + "right_line": 1875 + }, + { + "left_line": 1867, + "right_line": 1876 + }, + { + "left_line": 1868, + "right_line": 1877 + }, + { + "left_line": 1869, + "right_line": 1878 + }, + { + "left_line": 1870, + "right_line": 1879 + }, + { + "left_line": 1871, + "right_line": 1880 + }, + { + "left_line": 1872, + "right_line": 1881 + }, + { + "left_line": 1873, + "right_line": 1882 + }, + { + "left_line": 1874, + "right_line": 1883 + }, + { + "left_line": 1875, + "right_line": 1884 + }, + { + "left_line": 1876, + "right_line": 1885 + }, + { + "left_line": 1877, + "right_line": 1886 + }, + { + "left_line": 1878, + "right_line": 1887 + }, + { + "left_line": 1879, + "right_line": 1888 + }, + { + "left_line": 1880, + "right_line": 1889 + }, + { + "left_line": 1881, + "right_line": 1890 + }, + { + "left_line": 1882, + "right_line": 1891 + }, + { + "left_line": 1883, + "right_line": 1892 + }, + { + "left_line": 1884, + "right_line": 1893 + }, + { + "left_line": 1885, + "right_line": 1894 + }, + { + "left_line": 1886, + "right_line": 1895 + }, + { + "left_line": 1887, + "right_line": 1896 + }, + { + "left_line": 1888, + "right_line": 1897 + }, + { + "left_line": 1889, + "right_line": 1898 + }, + { + "left_line": 1890, + "right_line": 1899 + }, + { + "left_line": 1891, + "right_line": 1900 + }, + { + "left_line": 1892, + "right_line": 1901 + }, + { + "left_line": 1893, + "right_line": 1902 + }, + { + "left_line": 1894, + "right_line": 1903 + }, + { + "left_line": 1895, + "right_line": 1904 + }, + { + "left_line": 1896, + "right_line": 1905 + }, + { + "left_line": 1897, + "right_line": 1906 + }, + { + "left_line": 1898, + "right_line": 1907 + }, + { + "left_line": 1899, + "right_line": 1908 + }, + { + "left_line": 1900, + "right_line": 1909 + }, + { + "left_line": 1901, + "right_line": 1910 + }, + { + "left_line": 1902, + "right_line": 1911 + }, + { + "left_line": 1903, + "right_line": 1912 + }, + { + "left_line": 1904, + "right_line": 1913 + }, + { + "left_line": 1905, + "right_line": 1914 + }, + { + "left_line": 1906, + "right_line": 1915 + }, + { + "left_line": 1907, + "right_line": 1916 + }, + { + "left_line": 1908, + "right_line": 1917 + }, + { + "left_line": 1909, + "right_line": 1918 + }, + { + "left_line": 1910, + "right_line": 1919 + }, + { + "left_line": 1911, + "right_line": 1920 + }, + { + "left_line": 1912, + "right_line": 1921 + }, + { + "left_line": 1913, + "right_line": 1922 + }, + { + "left_line": 1914, + "right_line": 1923 + }, + { + "left_line": 1915, + "right_line": 1924 + }, + { + "left_line": 1916, + "right_line": 1925 + }, + { + "left_line": 1917, + "right_line": 1926 + }, + { + "left_line": 1918, + "right_line": 1927 + }, + { + "left_line": 1919, + "right_line": 1928 + }, + { + "left_line": 1920, + "right_line": 1929 + }, + { + "left_line": 1921, + "right_line": 1930 + }, + { + "left_line": 1922, + "right_line": 1931 + }, + { + "left_line": 1923, + "right_line": 1932 + }, + { + "left_line": 1924, + "right_line": 1933 + }, + { + "left_line": 1925, + "right_line": 1934 + }, + { + "left_line": 1926, + "right_line": 1935 + }, + { + "left_line": 1927, + "right_line": 1936 + }, + { + "left_line": 1928, + "right_line": 1937 + }, + { + "left_line": 1929, + "right_line": 1938 + }, + { + "left_line": 1930, + "right_line": 1939 + }, + { + "left_line": 1931, + "right_line": 1940 + }, + { + "left_line": 1932, + "right_line": 1941 + }, + { + "left_line": 1933, + "right_line": 1942 + }, + { + "left_line": 1934, + "right_line": 1943 + }, + { + "left_line": 1935, + "right_line": 1944 + }, + { + "left_line": 1936, + "right_line": 1945 + }, + { + "left_line": 1937, + "right_line": 1946 + }, + { + "left_line": 1938, + "right_line": 1947 + }, + { + "left_line": 1939, + "right_line": 1948 + }, + { + "left_line": 1940, + "right_line": 1949 + }, + { + "left_line": 1941, + "right_line": 1950 + }, + { + "left_line": 1942, + "right_line": 1951 + }, + { + "left_line": 1943, + "right_line": 1952 + }, + { + "left_line": 1944, + "right_line": 1953 + }, + { + "left_line": 1945, + "right_line": 1954 + }, + { + "left_line": 1946, + "right_line": 1955 + }, + { + "left_line": 1947, + "right_line": 1956 + }, + { + "left_line": 1948, + "right_line": 1957 + }, + { + "left_line": 1949, + "right_line": 1958 + }, + { + "left_line": 1950, + "right_line": 1959 + }, + { + "left_line": 1951, + "right_line": 1960 + }, + { + "left_line": 1952, + "right_line": 1961 + }, + { + "left_line": 1953, + "right_line": 1962 + }, + { + "left_line": 1954, + "right_line": 1963 + }, + { + "left_line": 1955, + "right_line": 1964 + }, + { + "left_line": 1956, + "right_line": 1965 + }, + { + "left_line": 1957, + "right_line": 1966 + }, + { + "left_line": 1958, + "right_line": 1967 + }, + { + "left_line": 1959, + "right_line": 1968 + }, + { + "left_line": 1960, + "right_line": 1969 + }, + { + "left_line": 1961, + "right_line": 1970 + }, + { + "left_line": 1962, + "right_line": 1971 + }, + { + "left_line": 1963, + "right_line": 1972 + }, + { + "left_line": 1964, + "right_line": 1973 + }, + { + "left_line": 1965, + "right_line": 1974 + }, + { + "left_line": 1966, + "right_line": 1975 + }, + { + "left_line": 1967, + "right_line": 1976 + }, + { + "left_line": 1968, + "right_line": 1977 + }, + { + "left_line": 1969, + "right_line": 1978 + }, + { + "left_line": 1970, + "right_line": 1979 + }, + { + "left_line": 1971, + "right_line": 1980 + }, + { + "left_line": 1972, + "right_line": 1981 + }, + { + "left_line": 1973, + "right_line": 1982 + }, + { + "left_line": 1974, + "right_line": 1983 + }, + { + "left_line": 1975, + "right_line": 1984 + }, + { + "left_line": 1976, + "right_line": 1985 + }, + { + "left_line": 1977, + "right_line": 1986 + }, + { + "left_line": 1978, + "right_line": 1987 + }, + { + "left_line": 1979, + "right_line": 1988 + }, + { + "left_line": 1980, + "right_line": 1989 + }, + { + "left_line": 1981, + "right_line": 1990 + }, + { + "left_line": 1982, + "right_line": 1991 + }, + { + "left_line": 1983, + "right_line": 1992 + }, + { + "left_line": 1984, + "right_line": 1993 + }, + { + "left_line": 1985, + "right_line": 1994 + }, + { + "left_line": 1986, + "right_line": 1995 + }, + { + "left_line": 1987, + "right_line": 1996 + }, + { + "left_line": 1988, + "right_line": 1997 + }, + { + "left_line": 1989, + "right_line": 1998 + }, + { + "left_line": 1990, + "right_line": 1999 + }, + { + "left_line": 1991, + "right_line": 2000 + }, + { + "left_line": 1992, + "right_line": 2001 + }, + { + "left_line": 1993, + "right_line": 2002 + }, + { + "left_line": 1994, + "right_line": 2003 + }, + { + "left_line": 1995, + "right_line": 2004 + }, + { + "left_line": 1996, + "right_line": 2005 + }, + { + "left_line": 1997, + "right_line": 2006 + }, + { + "left_line": 1998, + "right_line": 2007 + }, + { + "left_line": 1999, + "right_line": 2008 + }, + { + "left_line": 2000, + "right_line": 2009 + }, + { + "left_line": 2001, + "right_line": 2010 + }, + { + "left_line": 2002, + "right_line": 2011 + }, + { + "left_line": 2003, + "right_line": 2012 + }, + { + "left_line": 2004, + "right_line": 2013 + }, + { + "left_line": 2005, + "right_line": 2014 + }, + { + "left_line": 2006, + "right_line": 2015 + }, + { + "left_line": 2007, + "right_line": 2016 + }, + { + "left_line": 2008, + "right_line": 2017 + }, + { + "left_line": 2009, + "right_line": 2018 + }, + { + "left_line": 2010, + "right_line": 2019 + }, + { + "left_line": 2011, + "right_line": 2020 + }, + { + "left_line": 2012, + "right_line": 2021 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/metadata.json b/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/metadata.json new file mode 100644 index 0000000..39717e8 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "39488d93beb0ce47f6fb86d4ee00c927e32455b0", + "commit": "97b41fc616678de5b85165f4d6093db96739568d", + "message": "Fix memory leaks for proc template (#1719)", + "author": "Slevin", + "date": "2021-08-31T10:55:07+08:00", + "language": "ruby", + "files_changed": 1, + "insertions": 22, + "deletions": 13 +} diff --git a/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/new.rb b/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/new.rb new file mode 100644 index 0000000..70a22d6 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/new.rb @@ -0,0 +1,2021 @@ +# coding: utf-8 +# frozen_string_literal: true + +# external dependencies +require 'rack' +require 'tilt' +require 'rack/protection' +require 'mustermann' +require 'mustermann/sinatra' +require 'mustermann/regular' + +# stdlib dependencies +require 'thread' +require 'time' +require 'uri' + +# other files we need +require 'sinatra/indifferent_hash' +require 'sinatra/show_exceptions' +require 'sinatra/version' + +module Sinatra + # The request object. See Rack::Request for more info: + # http://rubydoc.info/github/rack/rack/master/Rack/Request + class Request < Rack::Request + HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/ + HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*/ + + # Returns an array of acceptable media types for the response + def accept + @env['sinatra.accept'] ||= begin + if @env.include? 'HTTP_ACCEPT' and @env['HTTP_ACCEPT'].to_s != '' + @env['HTTP_ACCEPT'].to_s.scan(HEADER_VALUE_WITH_PARAMS). + map! { |e| AcceptEntry.new(e) }.sort + else + [AcceptEntry.new('*/*')] + end + end + end + + def accept?(type) + preferred_type(type).to_s.include?(type) + end + + def preferred_type(*types) + return accept.first if types.empty? + types.flatten! + return types.first if accept.empty? + accept.detect do |accept_header| + type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) } + return type if type + end + end + + alias secure? ssl? + + def forwarded? + @env.include? "HTTP_X_FORWARDED_HOST" + end + + def safe? + get? or head? or options? or trace? + end + + def idempotent? + safe? or put? or delete? or link? or unlink? + end + + def link? + request_method == "LINK" + end + + def unlink? + request_method == "UNLINK" + end + + def params + super + rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e + raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}" + end + + class AcceptEntry + attr_accessor :params + attr_reader :entry + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @entry = entry + @type = entry[/[^;]+/].delete(' ') + @params = Hash[params] + @q = @params.delete('q') { 1.0 }.to_f + end + + def <=>(other) + other.priority <=> self.priority + end + + def priority + # We sort in descending order; better matches should be higher. + [ @q, -@type.count('*'), @params.size ] + end + + def to_str + @type + end + + def to_s(full = false) + full ? entry : to_str + end + + def respond_to?(*args) + super or to_str.respond_to?(*args) + end + + def method_missing(*args, &block) + to_str.send(*args, &block) + end + end + + class MimeTypeEntry + attr_reader :params + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @type = entry[/[^;]+/].delete(' ') + @params = Hash[params] + end + + def accepts?(entry) + File.fnmatch(entry, self) && matches_params?(entry.params) + end + + def to_str + @type + end + + def matches_params?(params) + return true if @params.empty? + + params.all? { |k,v| !@params.has_key?(k) || @params[k] == v } + end + end + end + + # The response object. See Rack::Response and Rack::Response::Helpers for + # more info: + # http://rubydoc.info/github/rack/rack/master/Rack/Response + # http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers + class Response < Rack::Response + DROP_BODY_RESPONSES = [204, 304] + + def body=(value) + value = value.body while Rack::Response === value + @body = String === value ? [value.to_str] : value + end + + def each + block_given? ? super : enum_for(:each) + end + + def finish + result = body + + if drop_content_info? + headers.delete "Content-Length" + headers.delete "Content-Type" + end + + if drop_body? + close + result = [] + end + + if calculate_content_length? + # if some other code has already set Content-Length, don't muck with it + # currently, this would be the static file-handler + headers["Content-Length"] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] + end + + private + + def calculate_content_length? + headers["Content-Type"] and not headers["Content-Length"] and Array === body + end + + def drop_content_info? + informational? or drop_body? + end + + def drop_body? + DROP_BODY_RESPONSES.include?(status) + end + end + + # Some Rack handlers (Rainbows!) implement an extended body object protocol, however, + # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question. + # This middleware will detect an extended body object and will make sure it reaches the + # handler directly. We do this here, so our middleware and middleware set up by the app will + # still be able to run. + class ExtendedRack < Struct.new(:app) + def call(env) + result, callback = app.call(env), env['async.callback'] + return result unless callback and async?(*result) + after_response { callback.call result } + setup_close(env, *result) + throw :async + end + + private + + def setup_close(env, status, headers, body) + return unless body.respond_to? :close and env.include? 'async.close' + env['async.close'].callback { body.close } + env['async.close'].errback { body.close } + end + + def after_response(&block) + raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine + EventMachine.next_tick(&block) + end + + def async?(status, headers, body) + return true if status == -1 + body.respond_to? :callback and body.respond_to? :errback + end + end + + # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing, + # if another CommonLogger is already in the middleware chain. + class CommonLogger < Rack::CommonLogger + def call(env) + env['sinatra.commonlogger'] ? @app.call(env) : super + end + + superclass.class_eval do + alias call_without_check call unless method_defined? :call_without_check + def call(env) + env['sinatra.commonlogger'] = true + call_without_check(env) + end + end + end + + class BadRequest < TypeError #:nodoc: + def http_status; 400 end + end + + class NotFound < NameError #:nodoc: + def http_status; 404 end + end + + # Methods available to routes, before/after filters, and views. + module Helpers + # Set or retrieve the response status code. + def status(value = nil) + response.status = Rack::Utils.status_code(value) if value + response.status + end + + # Set or retrieve the response body. When a block is given, + # evaluation is deferred until the body is read with #each. + def body(value = nil, &block) + if block_given? + def block.each; yield(call) end + response.body = block + elsif value + # Rack 2.0 returns a Rack::File::Iterator here instead of + # Rack::File as it was in the previous API. + unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream) + headers.delete 'Content-Length' + end + response.body = value + else + response.body + end + end + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) + if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET' + status 303 + else + status 302 + end + + # According to RFC 2616 section 14.30, "the field value consists of a + # single absolute URI" + response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?) + halt(*args) + end + + # Generates the absolute URI for a given path in the app. + # Takes Rack routers and reverse proxies into account. + def uri(addr = nil, absolute = true, add_script_name = true) + return addr if addr =~ /\A[a-z][a-z0-9\+\.\-]*:/i + uri = [host = String.new] + if absolute + host << "http#{'s' if request.secure?}://" + if request.forwarded? or request.port != (request.secure? ? 443 : 80) + host << request.host_with_port + else + host << request.host + end + end + uri << request.script_name.to_s if add_script_name + uri << (addr ? addr : request.path_info).to_s + File.join uri + end + + alias url uri + alias to uri + + # Halt processing and return the error status provided. + def error(code, body = nil) + code, body = 500, code.to_str if code.respond_to? :to_str + response.body = body unless body.nil? + halt code + end + + # Halt processing and return a 404 Not Found. + def not_found(body = nil) + error 404, body + end + + # Set multiple response headers with Hash. + def headers(hash = nil) + response.headers.merge! hash if hash + response.headers + end + + # Access the underlying Rack session. + def session + request.session + end + + # Access shared logger object. + def logger + request.logger + end + + # Look up a media type by file extension in Rack's mime registry. + def mime_type(type) + Base.mime_type(type) + end + + # Set the Content-Type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) + return response['Content-Type'] unless type + default = params.delete :default + mime_type = mime_type(type) || default + fail "Unknown media type: %p" % type if mime_type.nil? + mime_type = mime_type.dup + unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type } + params[:charset] = params.delete('charset') || settings.default_encoding + end + params.delete :charset if mime_type.include? 'charset' + unless params.empty? + mime_type << (mime_type.include?(';') ? ', ' : ';') + mime_type << params.map do |key, val| + val = val.inspect if val =~ /[";,]/ + "#{key}=#{val}" + end.join(', ') + end + response['Content-Type'] = mime_type + end + + # Set the Content-Disposition to "attachment" with the specified filename, + # instructing the user agents to prompt to save. + def attachment(filename = nil, disposition = :attachment) + response['Content-Disposition'] = disposition.to_s.dup + if filename + params = '; filename="%s"' % File.basename(filename) + response['Content-Disposition'] << params + ext = File.extname(filename) + content_type(ext) unless response['Content-Type'] or ext.empty? + end + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) + if opts[:type] or not response['Content-Type'] + content_type opts[:type] || File.extname(path), :default => 'application/octet-stream' + end + + disposition = opts[:disposition] + filename = opts[:filename] + disposition = :attachment if disposition.nil? and filename + filename = path if filename.nil? + attachment(filename, disposition) if disposition + + last_modified opts[:last_modified] if opts[:last_modified] + + file = Rack::File.new(File.dirname(settings.app_file)) + result = file.serving(request, path) + + result[1].each { |k,v| headers[k] ||= v } + headers['Content-Length'] = result[1]['Content-Length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT + not_found + end + + # Class of the response body in case you use #stream. + # + # Three things really matter: The front and back block (back being the + # block generating content, front the one sending it to the client) and + # the scheduler, integrating with whatever concurrency feature the Rack + # handler is using. + # + # Scheduler has to respond to defer and schedule. + class Stream + def self.schedule(*) yield end + def self.defer(*) yield end + + def initialize(scheduler = self.class, keep_open = false, &back) + @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open + @callbacks, @closed = [], false + end + + def close + return if closed? + @closed = true + @scheduler.schedule { @callbacks.each { |c| c.call } } + end + + def each(&front) + @front = front + @scheduler.defer do + begin + @back.call(self) + rescue Exception => e + @scheduler.schedule { raise e } + end + close unless @keep_open + end + end + + def <<(data) + @scheduler.schedule { @front.call(data.to_s) } + self + end + + def callback(&block) + return yield if closed? + @callbacks << block + end + + alias errback callback + + def closed? + @closed + end + end + + # Allows to start sending data to the client even though later parts of + # the response body have not yet been generated. + # + # The close parameter specifies whether Stream#close should be called + # after the block has been executed. This is only relevant for evented + # servers like Rainbows. + def stream(keep_open = false) + scheduler = env['async.callback'] ? EventMachine : Stream + current = @params.dup + body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } + end + + # Specify response freshness policy for HTTP caches (Cache-Control header). + # Any number of non-value directives (:public, :private, :no_cache, + # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with + # a Hash of value directives (:max_age, :s_maxage). + # + # cache_control :public, :must_revalidate, :max_age => 60 + # => Cache-Control: public, must-revalidate, max-age=60 + # + # See RFC 2616 / 14.9 for more on standard cache control directives: + # http://tools.ietf.org/html/rfc2616#section-14.9.1 + def cache_control(*values) + if values.last.kind_of?(Hash) + hash = values.pop + hash.reject! { |k, v| v == false } + hash.reject! { |k, v| values << k if v == true } + else + hash = {} + end + + values.map! { |value| value.to_s.tr('_','-') } + hash.each do |key, value| + key = key.to_s.tr('_', '-') + value = value.to_i if ['max-age', 's-maxage'].include? key + values << "#{key}=#{value}" + end + + response['Cache-Control'] = values.join(', ') if values.any? + end + + # Set the Expires header and Cache-Control/max-age directive. Amount + # can be an integer number of seconds in the future or a Time object + # indicating when the response should be considered "stale". The remaining + # "values" arguments are passed to the #cache_control helper: + # + # expires 500, :public, :must_revalidate + # => Cache-Control: public, must-revalidate, max-age=500 + # => Expires: Mon, 08 Jun 2009 08:50:17 GMT + # + def expires(amount, *values) + values << {} unless values.last.kind_of?(Hash) + + if amount.is_a? Integer + time = Time.now + amount.to_i + max_age = amount + else + time = time_for amount + max_age = time - Time.now + end + + values.last.merge!(:max_age => max_age) + cache_control(*values) + + response['Expires'] = time.httpdate + end + + # Set the last modified time of the resource (HTTP 'Last-Modified' header) + # and halt if conditional GET matches. The +time+ argument is a Time, + # DateTime, or other object that responds to +to_time+. + # + # When the current request includes an 'If-Modified-Since' header that is + # equal or later than the time specified, execution is immediately halted + # with a '304 Not Modified' response. + def last_modified(time) + return unless time + time = time_for time + response['Last-Modified'] = time.httpdate + return if env['HTTP_IF_NONE_MATCH'] + + if status == 200 and env['HTTP_IF_MODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i + halt 304 if since >= time.to_i + end + + if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i + halt 412 if since < time.to_i + end + rescue ArgumentError + end + + ETAG_KINDS = [:strong, :weak] + # Set the response entity tag (HTTP 'ETag' header) and halt if conditional + # GET matches. The +value+ argument is an identifier that uniquely + # identifies the current version of the resource. The +kind+ argument + # indicates whether the etag should be used as a :strong (default) or :weak + # cache validator. + # + # When the current request includes an 'If-None-Match' header with a + # matching etag, execution is immediately halted. If the request method is + # GET or HEAD, a '304 Not Modified' response is sent. + def etag(value, options = {}) + # Before touching this code, please double check RFC 2616 14.24 and 14.26. + options = {:kind => options} unless Hash === options + kind = options[:kind] || :strong + new_resource = options.fetch(:new_resource) { request.post? } + + unless ETAG_KINDS.include?(kind) + raise ArgumentError, ":strong or :weak expected" + end + + value = '"%s"' % value + value = "W/#{value}" if kind == :weak + response['ETag'] = value + + if success? or status == 304 + if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource + halt(request.safe? ? 304 : 412) + end + + if env['HTTP_IF_MATCH'] + halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource + end + end + end + + # Sugar for redirect (example: redirect back) + def back + request.referer + end + + # whether or not the status is set to 1xx + def informational? + status.between? 100, 199 + end + + # whether or not the status is set to 2xx + def success? + status.between? 200, 299 + end + + # whether or not the status is set to 3xx + def redirect? + status.between? 300, 399 + end + + # whether or not the status is set to 4xx + def client_error? + status.between? 400, 499 + end + + # whether or not the status is set to 5xx + def server_error? + status.between? 500, 599 + end + + # whether or not the status is set to 404 + def not_found? + status == 404 + end + + # whether or not the status is set to 400 + def bad_request? + status == 400 + end + + # Generates a Time object from the given value. + # Used by #expires and #last_modified. + def time_for(value) + if value.is_a? Numeric + Time.at value + elsif value.respond_to? :to_s + Time.parse value.to_s + else + value.to_time + end + rescue ArgumentError => boom + raise boom + rescue Exception + raise ArgumentError, "unable to convert #{value.inspect} to a Time object" + end + + private + + # Helper method checking if a ETag value list includes the current ETag. + def etag_matches?(list, new_resource = request.post?) + return !new_resource if list == '*' + list.to_s.split(/\s*,\s*/).include? response['ETag'] + end + + def with_params(temp_params) + original, @params = @params, temp_params + yield + ensure + @params = original if original + end + end + + # Template rendering methods. Each method takes the name of a template + # to render as a Symbol and returns a String with the rendered output, + # as well as an optional hash with additional options. + # + # `template` is either the name or path of the template as symbol + # (Use `:'subdir/myview'` for views in subdirectories), or a string + # that will be rendered. + # + # Possible options are: + # :content_type The content type to use, same arguments as content_type. + # :layout If set to something falsy, no layout is rendered, otherwise + # the specified layout is used (Ignored for `sass` and `less`) + # :layout_engine Engine to use for rendering the layout. + # :locals A hash with local variables that should be available + # in the template + # :scope If set, template is evaluate with the binding of the given + # object rather than the application instance. + # :views Views directory to use. + module Templates + module ContentTyped + attr_accessor :content_type + end + + def initialize + super + @default_layout = :layout + @preferred_extension = nil + end + + def erb(template, options = {}, locals = {}, &block) + render(:erb, template, options, locals, &block) + end + + def erubis(template, options = {}, locals = {}) + warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \ + "If you have Erubis installed, it will be used automatically." + render :erubis, template, options, locals + end + + def haml(template, options = {}, locals = {}, &block) + render(:haml, template, options, locals, &block) + end + + def sass(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :css + render :sass, template, options, locals + end + + def scss(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :css + render :scss, template, options, locals + end + + def less(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :css + render :less, template, options, locals + end + + def stylus(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :css + render :styl, template, options, locals + end + + def builder(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:builder, template, options, locals, &block) + end + + def liquid(template, options = {}, locals = {}, &block) + render(:liquid, template, options, locals, &block) + end + + def markdown(template, options = {}, locals = {}) + options[:exclude_outvar] = true + render :markdown, template, options, locals + end + + def textile(template, options = {}, locals = {}) + render :textile, template, options, locals + end + + def rdoc(template, options = {}, locals = {}) + render :rdoc, template, options, locals + end + + def asciidoc(template, options = {}, locals = {}) + render :asciidoc, template, options, locals + end + + def radius(template, options = {}, locals = {}) + render :radius, template, options, locals + end + + def markaby(template = nil, options = {}, locals = {}, &block) + render_ruby(:mab, template, options, locals, &block) + end + + def coffee(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :js + render :coffee, template, options, locals + end + + def nokogiri(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:nokogiri, template, options, locals, &block) + end + + def slim(template, options = {}, locals = {}, &block) + render(:slim, template, options, locals, &block) + end + + def creole(template, options = {}, locals = {}) + render :creole, template, options, locals + end + + def mediawiki(template, options = {}, locals = {}) + render :mediawiki, template, options, locals + end + + def wlang(template, options = {}, locals = {}, &block) + render(:wlang, template, options, locals, &block) + end + + def yajl(template, options = {}, locals = {}) + options[:default_content_type] = :json + render :yajl, template, options, locals + end + + def rabl(template, options = {}, locals = {}) + Rabl.register! + render :rabl, template, options, locals + end + + # Calls the given block for every possible template file in views, + # named name.ext, where ext is registered on engine. + def find_template(views, name, engine) + yield ::File.join(views, "#{name}.#{@preferred_extension}") + + Tilt.default_mapping.extensions_for(engine).each do |ext| + yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension + end + end + + private + + # logic shared between builder and nokogiri + def render_ruby(engine, template, options = {}, locals = {}, &block) + options, template = template, nil if template.is_a?(Hash) + template = Proc.new { block } if template.nil? + render engine, template, options, locals + end + + def render(engine, data, options = {}, locals = {}, &block) + # merge app-level options + engine_options = settings.respond_to?(engine) ? settings.send(engine) : {} + options.merge!(engine_options) { |key, v1, v2| v1 } + + # extract generic options + locals = options.delete(:locals) || locals || {} + views = options.delete(:views) || settings.views || "./views" + layout = options[:layout] + layout = false if layout.nil? && options.include?(:layout) + eat_errors = layout.nil? + layout = engine_options[:layout] if layout.nil? or (layout == true && engine_options[:layout] != false) + layout = @default_layout if layout.nil? or layout == true + layout_options = options.delete(:layout_options) || {} + content_type = options.delete(:default_content_type) + content_type = options.delete(:content_type) || content_type + layout_engine = options.delete(:layout_engine) || engine + scope = options.delete(:scope) || self + exclude_outvar = options.delete(:exclude_outvar) + options.delete(:layout) + + # set some defaults + options[:outvar] ||= '@_out_buf' unless exclude_outvar + options[:default_encoding] ||= settings.default_encoding + + # compile and render template + begin + layout_was = @default_layout + @default_layout = false + template = compile_template(engine, data, options, views) + output = template.render(scope, locals, &block) + ensure + @default_layout = layout_was + end + + # render layout + if layout + options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope). + merge!(layout_options) + catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } } + end + + output.extend(ContentTyped).content_type = content_type if content_type + output + end + + def compile_template(engine, data, options, views) + eat_errors = options.delete :eat_errors + template = Tilt[engine] + raise "Template engine not found: #{engine}" if template.nil? + + case data + when Symbol + template_cache.fetch engine, data, options, views do + body, path, line = settings.templates[data] + if body + body = body.call if body.respond_to?(:call) + template.new(path, line.to_i, options) { body } + else + found = false + @preferred_extension = engine.to_s + find_template(views, data, template) do |file| + path ||= file # keep the initial path rather than the last one + if found = File.exist?(file) + path = file + break + end + end + throw :layout_missing if eat_errors and not found + template.new(path, 1, options) + end + end + when Proc + compile_block_template(template, options, &data) + when String + template_cache.fetch engine, data, options, views do + compile_block_template(template, options) { data } + end + else + raise ArgumentError, "Sorry, don't know how to render #{data.inspect}." + end + end + + private + + def compile_block_template(template, options, &body) + caller = settings.caller_locations.first + path = options[:path] || caller[0] + line = options[:line] || caller[1] + template.new(path, line.to_i, options, &body) + end + end + + # Base class for all Sinatra applications and middleware. + class Base + include Rack::Utils + include Helpers + include Templates + + URI_INSTANCE = URI::Parser.new + + attr_accessor :app, :env, :request, :response, :params + attr_reader :template_cache + + def initialize(app = nil, **kwargs) + super() + @app = app + @template_cache = Tilt::Cache.new + @pinned_response = nil # whether a before! filter pinned the content-type + yield self if block_given? + end + + # Rack call interface. + def call(env) + dup.call!(env) + end + + def call!(env) # :nodoc: + @env = env + @params = IndifferentHash.new + @request = Request.new(env) + @response = Response.new + @pinned_response = nil + template_cache.clear if settings.reload_templates + + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + + unless @response['Content-Type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif default = settings.default_content_type + content_type default + end + end + + @response.finish + end + + # Access settings defined with Base.set. + def self.settings + self + end + + # Access settings defined with Base.set. + def settings + self.class.settings + end + + def options + warn "Sinatra::Base#options is deprecated and will be removed, " \ + "use #settings instead." + settings + end + + # Exit the current block, halts any further processing + # of the request, and returns the specified response. + def halt(*response) + response = response.first if response.length == 1 + throw :halt, response + end + + # Pass control to the next matching route. + # If there are no more matching routes, Sinatra will + # return a 404 response. + def pass(&block) + throw :pass, block + end + + # Forward the request to the downstream app -- middleware only. + def forward + fail "downstream app not set" unless @app.respond_to? :call + status, headers, body = @app.call env + @response.status = status + @response.body = body + @response.headers.merge! headers + nil + end + + private + + # Run filters defined on the class and all superclasses. + # Accepts an optional block to call after each filter is applied. + def filter!(type, base = settings, &block) + filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters) + base.filters[type].each do |args| + result = process_route(*args) + block.call(result) if block_given? + end + end + + # Run routes defined on the class and all superclasses. + def route!(base = settings, pass_block = nil) + if routes = base.routes[@request.request_method] + routes.each do |pattern, conditions, block| + response.delete_header('Content-Type') unless @pinned_response + + returned_pass_block = process_route(pattern, conditions) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" + route_eval { block[*args] } + end + + # don't wipe out pass_block in superclass + pass_block = returned_pass_block if returned_pass_block + end + end + + # Run routes defined in superclass. + if base.superclass.respond_to?(:routes) + return route!(base.superclass, pass_block) + end + + route_eval(&pass_block) if pass_block + route_missing + end + + # Run a route block and throw :halt with the result. + def route_eval + throw :halt, yield + end + + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, conditions, block = nil, values = []) + route = @request.path_info + route = '/' if route.empty? and not settings.empty_path_info? + route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/') + return unless params = pattern.params(route) + + params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes + force_encoding(params) + @params = @params.merge(params) if params.any? + + regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} ) + if regexp_exists + captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c } + values += captures + @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty? + else + values += params.values.flatten + end + + catch(:pass) do + conditions.each { |c| throw :pass if c.bind(self).call == false } + block ? block[self, values] : yield(self, values) + end + rescue + @env['sinatra.error.params'] = @params + raise + ensure + params ||= {} + params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params'] + end + + # No matching route was found or all routes passed. The default + # implementation is to forward the request downstream when running + # as middleware (@app is non-nil); when no downstream app is set, raise + # a NotFound exception. Subclasses can override this method to perform + # custom route miss logic. + def route_missing + if @app + forward + else + raise NotFound, "#{request.request_method} #{request.path_info}" + end + end + + # Attempt to serve static files from public directory. Throws :halt when + # a matching file is found, returns nil otherwise. + def static!(options = {}) + return if (public_dir = settings.public_folder).nil? + path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" + return unless valid_path?(path) + + path = File.expand_path(path) + return unless path.start_with?(File.expand_path(public_dir) + '/') + return unless File.file?(path) + + env['sinatra.static_file'] = path + cache_control(*settings.static_cache_control) if settings.static_cache_control? + send_file path, options.merge(:disposition => nil) + end + + # Run the block with 'throw :halt' support and apply result to the response. + def invoke + res = catch(:halt) { yield } + + res = [res] if Integer === res or String === res + if Array === res and Integer === res.first + res = res.dup + status(res.shift) + body(res.pop) + headers(*res) + elsif res.respond_to? :each + body res + end + nil # avoid double setting the same response tuple twice + end + + # Dispatch a request with error handling. + def dispatch! + # Avoid passing frozen string in force_encoding + @params.merge!(@request.params).each do |key, val| + next unless val.respond_to?(:force_encoding) + val = val.dup if val.frozen? + @params[key] = force_encoding(val) + end + + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do + @pinned_response = !response['Content-Type'].nil? + end + route! + end + rescue ::Exception => boom + invoke { handle_exception!(boom) } + ensure + begin + filter! :after unless env['sinatra.static_file'] + rescue ::Exception => boom + invoke { handle_exception!(boom) } unless @env['sinatra.error'] + end + end + + # Error handling during requests. + def handle_exception!(boom) + if error_params = @env['sinatra.error.params'] + @params = @params.merge(error_params) + end + @env['sinatra.error'] = boom + + if boom.respond_to? :http_status and boom.http_status.between? 400, 599 + status(boom.http_status) + elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599 + status(boom.code) + else + status(500) + end + + if server_error? + dump_errors! boom if settings.dump_errors? + raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler + elsif not_found? + headers['X-Cascade'] = 'pass' if settings.x_cascade? + end + + if res = error_block!(boom.class, boom) || error_block!(status, boom) + return res + end + + if not_found? || bad_request? + if boom.message && boom.message != boom.class.name + body Rack::Utils.escape_html(boom.message) + else + content_type 'text/html' + body '

' + (not_found? ? 'Not Found' : 'Bad Request') + '

' + end + end + + return unless server_error? + raise boom if settings.raise_errors? or settings.show_exceptions? + error_block! Exception, boom + end + + # Find an custom error block for the key(s) specified. + def error_block!(key, *block_params) + base = settings + while base.respond_to?(:errors) + next base = base.superclass unless args_array = base.errors[key] + args_array.reverse_each do |args| + first = args == args_array.first + args += [block_params] + resp = process_route(*args) + return resp unless resp.nil? && !first + end + end + return false unless key.respond_to? :superclass and key.superclass < Exception + error_block!(key.superclass, *block_params) + end + + def dump_errors!(boom) + msg = ["#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t") + @env['rack.errors'].puts(msg) + end + + class << self + CALLERS_TO_IGNORE = [ # :nodoc: + /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code + /lib\/tilt.*\.rb$/, # all tilt code + /^\(.*\)$/, # generated code + /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks + /active_support/, # active_support require hacks + /bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks + /= 1.9.2 + /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files + ] + + # contrary to what the comment said previously, rubinius never supported this + if defined?(RUBY_IGNORE_CALLERS) + warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0" + CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS) + end + + attr_reader :routes, :filters, :templates, :errors + + # Removes all routes, filters, middleware and extension hooks from the + # current class (not routes/filters/... defined by its superclass). + def reset! + @conditions = [] + @routes = {} + @filters = {:before => [], :after => []} + @errors = {} + @middleware = [] + @prototype = nil + @extensions = [] + + if superclass.respond_to?(:templates) + @templates = Hash.new { |hash, key| superclass.templates[key] } + else + @templates = {} + end + end + + # Extension modules registered on this class and all superclasses. + def extensions + if superclass.respond_to?(:extensions) + (@extensions + superclass.extensions).uniq + else + @extensions + end + end + + # Middleware used in this class and all superclasses. + def middleware + if superclass.respond_to?(:middleware) + superclass.middleware + @middleware + else + @middleware + end + end + + # Sets an option to the given value. If the value is a proc, + # the proc will be called every time the option is accessed. + def set(option, value = (not_set = true), ignore_setter = false, &block) + raise ArgumentError if block and !not_set + value, not_set = block, false if block + + if not_set + raise ArgumentError unless option.respond_to?(:each) + option.each { |k,v| set(k, v) } + return self + end + + if respond_to?("#{option}=") and not ignore_setter + return __send__("#{option}=", value) + end + + setter = proc { |val| set option, val, true } + getter = proc { value } + + case value + when Proc + getter = value + when Symbol, Integer, FalseClass, TrueClass, NilClass + getter = value.inspect + when Hash + setter = proc do |val| + val = value.merge val if Hash === val + set option, val, true + end + end + + define_singleton("#{option}=", setter) + define_singleton(option, getter) + define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?" + self + end + + # Same as calling `set :option, true` for each of the given options. + def enable(*opts) + opts.each { |key| set(key, true) } + end + + # Same as calling `set :option, false` for each of the given options. + def disable(*opts) + opts.each { |key| set(key, false) } + end + + # Define a custom error handler. Optionally takes either an Exception + # class, or an HTTP status code to specify which errors should be + # handled. + def error(*codes, &block) + args = compile! "ERROR", /.*/, block + codes = codes.flat_map(&method(:Array)) + codes << Exception if codes.empty? + codes << Sinatra::NotFound if codes.include?(404) + codes.each { |c| (@errors[c] ||= []) << args } + end + + # Sugar for `error(404) { ... }` + def not_found(&block) + error(404, &block) + end + + # Define a named template. The block must return the template source. + def template(name, &block) + filename, line = caller_locations.first + templates[name] = [block, filename, line.to_i] + end + + # Define the layout template. The block must return the template source. + def layout(name = :layout, &block) + template name, &block + end + + # Load embedded templates from the file; uses the caller's __FILE__ + # when no file is specified. + def inline_templates=(file = nil) + file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file + + begin + io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file) + app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2) + rescue Errno::ENOENT + app, data = nil + end + + if data + if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m + encoding = $2 + else + encoding = settings.default_encoding + end + lines = app.count("\n") + 1 + template = nil + force_encoding data, encoding + data.each_line do |line| + lines += 1 + if line =~ /^@@\s*(.*\S)\s*$/ + template = force_encoding(String.new, encoding) + templates[$1.to_sym] = [template, file, lines] + elsif template + template << line + end + end + end + end + + # Lookup or register a mime type in Rack's mime registry. + def mime_type(type, value = nil) + return type if type.nil? + return type.to_s if type.to_s.include?('/') + type = ".#{type}" unless type.to_s[0] == ?. + return Rack::Mime.mime_type(type, nil) unless value + Rack::Mime::MIME_TYPES[type] = value + end + + # provides all mime types matching type, including deprecated types: + # mime_types :html # => ['text/html'] + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type + type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type] + end + + # Define a before filter; runs before all requests within the same + # context as route handlers and may access/modify the request and + # response. + def before(path = /.*/, **options, &block) + add_filter(:before, path, **options, &block) + end + + # Define an after filter; runs after all requests within the same + # context as route handlers and may access/modify the request and + # response. + def after(path = /.*/, **options, &block) + add_filter(:after, path, **options, &block) + end + + # add a filter + def add_filter(type, path = /.*/, **options, &block) + filters[type] << compile!(type, path, block, **options) + end + + # Add a route condition. The route is considered non-matching when the + # block returns false. + def condition(name = "#{caller.first[/`.*'/]} condition", &block) + @conditions << generate_method(name, &block) + end + + def public=(value) + warn ":public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead" + set(:public_folder, value) + end + + def public_dir=(value) + self.public_folder = value + end + + def public_dir + public_folder + end + + # Defining a `GET` handler also automatically defines + # a `HEAD` handler. + def get(path, opts = {}, &block) + conditions = @conditions.dup + route('GET', path, opts, &block) + + @conditions = conditions + route('HEAD', path, opts, &block) + end + + def put(path, opts = {}, &bk) route 'PUT', path, opts, &bk end + def post(path, opts = {}, &bk) route 'POST', path, opts, &bk end + def delete(path, opts = {}, &bk) route 'DELETE', path, opts, &bk end + def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end + def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end + def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end + def link(path, opts = {}, &bk) route 'LINK', path, opts, &bk end + def unlink(path, opts = {}, &bk) route 'UNLINK', path, opts, &bk end + + # Makes the methods defined in the block and in the Modules given + # in `extensions` available to the handlers and templates + def helpers(*extensions, &block) + class_eval(&block) if block_given? + include(*extensions) if extensions.any? + end + + # Register an extension. Alternatively take a block from which an + # extension will be created and registered on the fly. + def register(*extensions, &block) + extensions << Module.new(&block) if block_given? + @extensions += extensions + extensions.each do |extension| + extend extension + extension.registered(self) if extension.respond_to?(:registered) + end + end + + def development?; environment == :development end + def production?; environment == :production end + def test?; environment == :test end + + # Set configuration options for Sinatra and/or the app. + # Allows scoping of settings for certain environments. + def configure(*envs) + yield self if envs.empty? || envs.include?(environment.to_sym) + end + + # Use the specified Rack middleware + def use(middleware, *args, &block) + @prototype = nil + @middleware << [middleware, args, block] + end + ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true) + + # Stop the self-hosted server if running. + def quit! + return unless running? + # Use Thin's hard #stop! if available, otherwise just #stop. + running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop + $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages? + set :running_server, nil + set :handler_name, nil + end + + alias_method :stop!, :quit! + + # Run the Sinatra app as a self-hosted server using + # Puma, Mongrel, or WEBrick (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + return if running? + set options + handler = Rack::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(:Port => port, :Host => bind) + + begin + start_server(handler, server_settings, handler_name, &block) + rescue Errno::EADDRINUSE + $stderr.puts "== Someone is already performing on port #{port}!" + raise + ensure + quit! + end + end + + alias_method :start!, :run! + + # Check whether the self-hosted server is running or not. + def running? + running_server? + end + + # The prototype instance used to process requests. + def prototype + @prototype ||= new + end + + # Create a new instance without middleware in front of it. + alias new! new unless method_defined? :new! + + # Create a new instance of the class fronted by its middleware + # pipeline. The object is guaranteed to respond to #call but may not be + # an instance of the class new was called on. + def new(*args, **kwargs, &bk) + instance = new!(*args, **kwargs, &bk) + Wrapper.new(build(instance).to_app, instance) + end + + # Creates a Rack::Builder instance with all the middleware set up and + # the given +app+ as end point. + def build(app) + builder = Rack::Builder.new + setup_default_middleware builder + setup_middleware builder + builder.run app + builder + end + + def call(env) + synchronize { prototype.call(env) } + end + + # Like Kernel#caller but excluding certain magic entries and without + # line / method information; the resulting array contains filenames only. + def caller_files + cleaned_caller(1).flatten + end + + # Like caller_files, but containing Arrays rather than strings with the + # first element being the file, and the second being the line. + def caller_locations + cleaned_caller 2 + end + + private + + # Starts the server by running the Rack Handler. + def start_server(handler, server_settings, handler_name) + # Ensure we initialize middleware before startup, to match standard Rack + # behavior, by ensuring an instance exists: + prototype + # Run the instance we created: + handler.run(self, **server_settings) do |server| + unless suppress_messages? + $stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}" + end + + setup_traps + set :running_server, server + set :handler_name, handler_name + server.threaded = settings.threaded if server.respond_to? :threaded= + + yield server if block_given? + end + end + + def suppress_messages? + handler_name =~ /cgi/i || quiet + end + + def setup_traps + if traps? + at_exit { quit! } + + [:INT, :TERM].each do |signal| + old_handler = trap(signal) do + quit! + old_handler.call if old_handler.respond_to?(:call) + end + end + + set :traps, false + end + end + + # Dynamically defines a method on settings. + def define_singleton(name, content = Proc.new) + singleton_class.class_eval do + undef_method(name) if method_defined? name + String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content) + end + end + + # Condition for matching host name. Parameter might be String or Regexp. + def host_name(pattern) + condition { pattern === request.host } + end + + # Condition for matching user agent. Parameter should be Regexp. + # Will set params[:agent]. + def user_agent(pattern) + condition do + if request.user_agent.to_s =~ pattern + @params[:agent] = $~[1..-1] + true + else + false + end + end + end + alias_method :agent, :user_agent + + # Condition for matching mimetypes. Accepts file extensions. + def provides(*types) + types.map! { |t| mime_types(t) } + types.flatten! + condition do + if type = response['Content-Type'] + types.include? type or types.include? type[/^[^;]+/] + elsif type = request.preferred_type(types) + params = (type.respond_to?(:params) ? type.params : {}) + content_type(type, params) + true + else + false + end + end + end + + def route(verb, path, options = {}, &block) + enable :empty_path_info if path == "" and empty_path_info.nil? + signature = compile!(verb, path, block, **options) + (@routes[verb] ||= []) << signature + invoke_hook(:route_added, verb, path, block) + signature + end + + def invoke_hook(name, *args) + extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } + end + + def generate_method(method_name, &block) + define_method(method_name, &block) + method = instance_method method_name + remove_method method_name + method + end + + def compile!(verb, path, block, **options) + # Because of self.options.host + host_name(options.delete(:host)) if options.key?(:host) + # Pass Mustermann opts to compile() + route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze + + options.each_pair { |option, args| send(option, *args) } + + pattern = compile(path, route_mustermann_opts) + method_name = "#{verb} #{path}" + unbound_method = generate_method(method_name, &block) + conditions, @conditions = @conditions, [] + wrapper = block.arity != 0 ? + proc { |a, p| unbound_method.bind(a).call(*p) } : + proc { |a, p| unbound_method.bind(a).call } + + [ pattern, conditions, wrapper ] + end + + def compile(path, route_mustermann_opts = {}) + Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts)) + end + + def setup_default_middleware(builder) + builder.use ExtendedRack + builder.use ShowExceptions if show_exceptions? + builder.use Rack::MethodOverride if method_override? + builder.use Rack::Head + setup_logging builder + setup_sessions builder + setup_protection builder + end + + def setup_middleware(builder) + middleware.each { |c,a,b| builder.use(c, *a, &b) } + end + + def setup_logging(builder) + if logging? + setup_common_logger(builder) + setup_custom_logger(builder) + elsif logging == false + setup_null_logger(builder) + end + end + + def setup_null_logger(builder) + builder.use Rack::NullLogger + end + + def setup_common_logger(builder) + builder.use Sinatra::CommonLogger + end + + def setup_custom_logger(builder) + if logging.respond_to? :to_int + builder.use Rack::Logger, logging + else + builder.use Rack::Logger + end + end + + def setup_protection(builder) + return unless protection? + options = Hash === protection ? protection.dup : {} + options = { + img_src: "'self' data:", + font_src: "'self'" + }.merge options + + protect_session = options.fetch(:session) { sessions? } + options[:without_session] = !protect_session + + options[:reaction] ||= :drop_session + + builder.use Rack::Protection, options + end + + def setup_sessions(builder) + return unless sessions? + options = {} + options[:secret] = session_secret if session_secret? + options.merge! sessions.to_hash if sessions.respond_to? :to_hash + builder.use session_store, options + end + + def inherited(subclass) + subclass.reset! + subclass.set :app_file, caller_files.first unless subclass.app_file? + super + end + + @@mutex = Mutex.new + def synchronize(&block) + if lock? + @@mutex.synchronize(&block) + else + yield + end + end + + # used for deprecation warnings + def warn(message) + super message + "\n\tfrom #{cleaned_caller.first.join(':')}" + end + + # Like Kernel#caller but excluding certain magic entries + def cleaned_caller(keep = 3) + caller(1). + map! { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }. + reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } } + end + end + + # Force data to specified encoding. It defaults to settings.default_encoding + # which is UTF-8 by default + def self.force_encoding(data, encoding = default_encoding) + return if data == settings || data.is_a?(Tempfile) + if data.respond_to? :force_encoding + data.force_encoding(encoding).encode! + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v, encoding) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v, encoding) } + end + data + end + + def force_encoding(*args) settings.force_encoding(*args) end + + reset! + + set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym + set :raise_errors, Proc.new { test? } + set :dump_errors, Proc.new { !test? } + set :show_exceptions, Proc.new { development? } + set :sessions, false + set :session_store, Rack::Session::Cookie + set :logging, false + set :protection, true + set :method_override, false + set :use_code, false + set :default_encoding, "utf-8" + set :x_cascade, true + set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" } + settings.add_charset << /^text\// + set :mustermann_opts, {} + set :default_content_type, 'text/html' + + # explicitly generating a session secret eagerly to play nice with preforking + begin + require 'securerandom' + set :session_secret, SecureRandom.hex(64) + rescue LoadError, NotImplementedError + # SecureRandom raises a NotImplementedError if no random device is available + set :session_secret, "%064x" % Kernel.rand(2**256-1) + end + + class << self + alias_method :methodoverride?, :method_override? + alias_method :methodoverride=, :method_override= + end + + set :run, false # start server via at-exit hook? + set :running_server, nil + set :handler_name, nil + set :traps, true + set :server, %w[HTTP webrick] + set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' } + set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567) + set :quiet, false + + ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + + if ruby_engine == 'macruby' + server.unshift 'control_tower' + else + server.unshift 'reel' + server.unshift 'puma' + server.unshift 'mongrel' if ruby_engine.nil? + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + end + + set :absolute_redirects, true + set :prefixed_redirects, false + set :empty_path_info, nil + set :strict_paths, true + + set :app_file, nil + set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) } + set :views, Proc.new { root && File.join(root, 'views') } + set :reload_templates, Proc.new { development? } + set :lock, false + set :threaded, true + + set :public_folder, Proc.new { root && File.join(root, 'public') } + set :static, Proc.new { public_folder && File.exist?(public_folder) } + set :static_cache_control, false + + error ::Exception do + response.status = 500 + content_type 'text/html' + '

Internal Server Error

' + end + + configure :development do + get '/__sinatra__/:image.png' do + filename = __dir__ + "/images/#{params[:image].to_i}.png" + content_type :png + send_file filename + end + + error NotFound do + content_type 'text/html' + + if self.class == Sinatra::Application + code = <<-RUBY.gsub(/^ {12}/, '') + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + RUBY + else + code = <<-RUBY.gsub(/^ {12}/, '') + class #{self.class} + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + end + RUBY + + file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '') + code = "# in #{file}\n#{code}" unless file.empty? + end + + (<<-HTML).gsub(/^ {10}/, '') + + + + + + +

Sinatra doesn’t know this ditty.

+ +
+ Try this: +
#{Rack::Utils.escape_html(code)}
+
+ + + HTML + end + end + end + + # Execution context for classic style (top-level) applications. All + # DSL methods executed on main are delegated to this class. + # + # The Application class should not be subclassed, unless you want to + # inherit all settings, routes, handlers, and error pages from the + # top-level. Subclassing Sinatra::Base is highly recommended for + # modular applications. + class Application < Base + set :logging, Proc.new { !test? } + set :method_override, true + set :run, Proc.new { !test? } + set :app_file, nil + + def self.register(*extensions, &block) #:nodoc: + added_methods = extensions.flat_map(&:public_instance_methods) + Delegator.delegate(*added_methods) + super(*extensions, &block) + end + end + + # Sinatra delegation mixin. Mixing this module into an object causes all + # methods to be delegated to the Sinatra::Application class. Used primarily + # at the top-level. + module Delegator #:nodoc: + def self.delegate(*methods) + methods.each do |method_name| + define_method(method_name) do |*args, &block| + return super(*args, &block) if respond_to? method_name + Delegator.target.send(method_name, *args, &block) + end + # ensure keyword argument passing is compatible with ruby >= 2.7 + ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true) + private method_name + end + end + + delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink, + :template, :layout, :before, :after, :error, :not_found, :configure, + :set, :mime_type, :enable, :disable, :use, :development?, :test?, + :production?, :helpers, :settings, :register + + class << self + attr_accessor :target + end + + self.target = Application + end + + class Wrapper + def initialize(stack, instance) + @stack, @instance = stack, instance + end + + def settings + @instance.settings + end + + def helpers + @instance + end + + def call(env) + @stack.call(env) + end + + def inspect + "#<#{@instance.class} app_file=#{settings.app_file.inspect}>" + end + end + + # Create a new Sinatra application; the block is evaluated in the class scope. + def self.new(base = Base, &block) + base = Class.new(base) + base.class_eval(&block) if block_given? + base + end + + # Extend the top-level DSL with the modules provided. + def self.register(*extensions, &block) + Delegator.target.register(*extensions, &block) + end + + # Include the helper modules provided in Sinatra's request context. + def self.helpers(*extensions, &block) + Delegator.target.helpers(*extensions, &block) + end + + # Use the middleware for classic applications. + def self.use(*args, &block) + Delegator.target.use(*args, &block) + end +end diff --git a/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/old.rb b/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/old.rb new file mode 100644 index 0000000..e28ea02 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_proc_template_memory_leak_9/old.rb @@ -0,0 +1,2012 @@ +# coding: utf-8 +# frozen_string_literal: true + +# external dependencies +require 'rack' +require 'tilt' +require 'rack/protection' +require 'mustermann' +require 'mustermann/sinatra' +require 'mustermann/regular' + +# stdlib dependencies +require 'thread' +require 'time' +require 'uri' + +# other files we need +require 'sinatra/indifferent_hash' +require 'sinatra/show_exceptions' +require 'sinatra/version' + +module Sinatra + # The request object. See Rack::Request for more info: + # http://rubydoc.info/github/rack/rack/master/Rack/Request + class Request < Rack::Request + HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/ + HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*/ + + # Returns an array of acceptable media types for the response + def accept + @env['sinatra.accept'] ||= begin + if @env.include? 'HTTP_ACCEPT' and @env['HTTP_ACCEPT'].to_s != '' + @env['HTTP_ACCEPT'].to_s.scan(HEADER_VALUE_WITH_PARAMS). + map! { |e| AcceptEntry.new(e) }.sort + else + [AcceptEntry.new('*/*')] + end + end + end + + def accept?(type) + preferred_type(type).to_s.include?(type) + end + + def preferred_type(*types) + return accept.first if types.empty? + types.flatten! + return types.first if accept.empty? + accept.detect do |accept_header| + type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) } + return type if type + end + end + + alias secure? ssl? + + def forwarded? + @env.include? "HTTP_X_FORWARDED_HOST" + end + + def safe? + get? or head? or options? or trace? + end + + def idempotent? + safe? or put? or delete? or link? or unlink? + end + + def link? + request_method == "LINK" + end + + def unlink? + request_method == "UNLINK" + end + + def params + super + rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e + raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}" + end + + class AcceptEntry + attr_accessor :params + attr_reader :entry + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @entry = entry + @type = entry[/[^;]+/].delete(' ') + @params = Hash[params] + @q = @params.delete('q') { 1.0 }.to_f + end + + def <=>(other) + other.priority <=> self.priority + end + + def priority + # We sort in descending order; better matches should be higher. + [ @q, -@type.count('*'), @params.size ] + end + + def to_str + @type + end + + def to_s(full = false) + full ? entry : to_str + end + + def respond_to?(*args) + super or to_str.respond_to?(*args) + end + + def method_missing(*args, &block) + to_str.send(*args, &block) + end + end + + class MimeTypeEntry + attr_reader :params + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @type = entry[/[^;]+/].delete(' ') + @params = Hash[params] + end + + def accepts?(entry) + File.fnmatch(entry, self) && matches_params?(entry.params) + end + + def to_str + @type + end + + def matches_params?(params) + return true if @params.empty? + + params.all? { |k,v| !@params.has_key?(k) || @params[k] == v } + end + end + end + + # The response object. See Rack::Response and Rack::Response::Helpers for + # more info: + # http://rubydoc.info/github/rack/rack/master/Rack/Response + # http://rubydoc.info/github/rack/rack/master/Rack/Response/Helpers + class Response < Rack::Response + DROP_BODY_RESPONSES = [204, 304] + + def body=(value) + value = value.body while Rack::Response === value + @body = String === value ? [value.to_str] : value + end + + def each + block_given? ? super : enum_for(:each) + end + + def finish + result = body + + if drop_content_info? + headers.delete "Content-Length" + headers.delete "Content-Type" + end + + if drop_body? + close + result = [] + end + + if calculate_content_length? + # if some other code has already set Content-Length, don't muck with it + # currently, this would be the static file-handler + headers["Content-Length"] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] + end + + private + + def calculate_content_length? + headers["Content-Type"] and not headers["Content-Length"] and Array === body + end + + def drop_content_info? + informational? or drop_body? + end + + def drop_body? + DROP_BODY_RESPONSES.include?(status) + end + end + + # Some Rack handlers (Rainbows!) implement an extended body object protocol, however, + # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question. + # This middleware will detect an extended body object and will make sure it reaches the + # handler directly. We do this here, so our middleware and middleware set up by the app will + # still be able to run. + class ExtendedRack < Struct.new(:app) + def call(env) + result, callback = app.call(env), env['async.callback'] + return result unless callback and async?(*result) + after_response { callback.call result } + setup_close(env, *result) + throw :async + end + + private + + def setup_close(env, status, headers, body) + return unless body.respond_to? :close and env.include? 'async.close' + env['async.close'].callback { body.close } + env['async.close'].errback { body.close } + end + + def after_response(&block) + raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine + EventMachine.next_tick(&block) + end + + def async?(status, headers, body) + return true if status == -1 + body.respond_to? :callback and body.respond_to? :errback + end + end + + # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing, + # if another CommonLogger is already in the middleware chain. + class CommonLogger < Rack::CommonLogger + def call(env) + env['sinatra.commonlogger'] ? @app.call(env) : super + end + + superclass.class_eval do + alias call_without_check call unless method_defined? :call_without_check + def call(env) + env['sinatra.commonlogger'] = true + call_without_check(env) + end + end + end + + class BadRequest < TypeError #:nodoc: + def http_status; 400 end + end + + class NotFound < NameError #:nodoc: + def http_status; 404 end + end + + # Methods available to routes, before/after filters, and views. + module Helpers + # Set or retrieve the response status code. + def status(value = nil) + response.status = Rack::Utils.status_code(value) if value + response.status + end + + # Set or retrieve the response body. When a block is given, + # evaluation is deferred until the body is read with #each. + def body(value = nil, &block) + if block_given? + def block.each; yield(call) end + response.body = block + elsif value + # Rack 2.0 returns a Rack::File::Iterator here instead of + # Rack::File as it was in the previous API. + unless request.head? || value.is_a?(Rack::File::Iterator) || value.is_a?(Stream) + headers.delete 'Content-Length' + end + response.body = value + else + response.body + end + end + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) + if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET' + status 303 + else + status 302 + end + + # According to RFC 2616 section 14.30, "the field value consists of a + # single absolute URI" + response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?) + halt(*args) + end + + # Generates the absolute URI for a given path in the app. + # Takes Rack routers and reverse proxies into account. + def uri(addr = nil, absolute = true, add_script_name = true) + return addr if addr =~ /\A[a-z][a-z0-9\+\.\-]*:/i + uri = [host = String.new] + if absolute + host << "http#{'s' if request.secure?}://" + if request.forwarded? or request.port != (request.secure? ? 443 : 80) + host << request.host_with_port + else + host << request.host + end + end + uri << request.script_name.to_s if add_script_name + uri << (addr ? addr : request.path_info).to_s + File.join uri + end + + alias url uri + alias to uri + + # Halt processing and return the error status provided. + def error(code, body = nil) + code, body = 500, code.to_str if code.respond_to? :to_str + response.body = body unless body.nil? + halt code + end + + # Halt processing and return a 404 Not Found. + def not_found(body = nil) + error 404, body + end + + # Set multiple response headers with Hash. + def headers(hash = nil) + response.headers.merge! hash if hash + response.headers + end + + # Access the underlying Rack session. + def session + request.session + end + + # Access shared logger object. + def logger + request.logger + end + + # Look up a media type by file extension in Rack's mime registry. + def mime_type(type) + Base.mime_type(type) + end + + # Set the Content-Type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) + return response['Content-Type'] unless type + default = params.delete :default + mime_type = mime_type(type) || default + fail "Unknown media type: %p" % type if mime_type.nil? + mime_type = mime_type.dup + unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type } + params[:charset] = params.delete('charset') || settings.default_encoding + end + params.delete :charset if mime_type.include? 'charset' + unless params.empty? + mime_type << (mime_type.include?(';') ? ', ' : ';') + mime_type << params.map do |key, val| + val = val.inspect if val =~ /[";,]/ + "#{key}=#{val}" + end.join(', ') + end + response['Content-Type'] = mime_type + end + + # Set the Content-Disposition to "attachment" with the specified filename, + # instructing the user agents to prompt to save. + def attachment(filename = nil, disposition = :attachment) + response['Content-Disposition'] = disposition.to_s.dup + if filename + params = '; filename="%s"' % File.basename(filename) + response['Content-Disposition'] << params + ext = File.extname(filename) + content_type(ext) unless response['Content-Type'] or ext.empty? + end + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) + if opts[:type] or not response['Content-Type'] + content_type opts[:type] || File.extname(path), :default => 'application/octet-stream' + end + + disposition = opts[:disposition] + filename = opts[:filename] + disposition = :attachment if disposition.nil? and filename + filename = path if filename.nil? + attachment(filename, disposition) if disposition + + last_modified opts[:last_modified] if opts[:last_modified] + + file = Rack::File.new(File.dirname(settings.app_file)) + result = file.serving(request, path) + + result[1].each { |k,v| headers[k] ||= v } + headers['Content-Length'] = result[1]['Content-Length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT + not_found + end + + # Class of the response body in case you use #stream. + # + # Three things really matter: The front and back block (back being the + # block generating content, front the one sending it to the client) and + # the scheduler, integrating with whatever concurrency feature the Rack + # handler is using. + # + # Scheduler has to respond to defer and schedule. + class Stream + def self.schedule(*) yield end + def self.defer(*) yield end + + def initialize(scheduler = self.class, keep_open = false, &back) + @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open + @callbacks, @closed = [], false + end + + def close + return if closed? + @closed = true + @scheduler.schedule { @callbacks.each { |c| c.call } } + end + + def each(&front) + @front = front + @scheduler.defer do + begin + @back.call(self) + rescue Exception => e + @scheduler.schedule { raise e } + end + close unless @keep_open + end + end + + def <<(data) + @scheduler.schedule { @front.call(data.to_s) } + self + end + + def callback(&block) + return yield if closed? + @callbacks << block + end + + alias errback callback + + def closed? + @closed + end + end + + # Allows to start sending data to the client even though later parts of + # the response body have not yet been generated. + # + # The close parameter specifies whether Stream#close should be called + # after the block has been executed. This is only relevant for evented + # servers like Rainbows. + def stream(keep_open = false) + scheduler = env['async.callback'] ? EventMachine : Stream + current = @params.dup + body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } + end + + # Specify response freshness policy for HTTP caches (Cache-Control header). + # Any number of non-value directives (:public, :private, :no_cache, + # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with + # a Hash of value directives (:max_age, :s_maxage). + # + # cache_control :public, :must_revalidate, :max_age => 60 + # => Cache-Control: public, must-revalidate, max-age=60 + # + # See RFC 2616 / 14.9 for more on standard cache control directives: + # http://tools.ietf.org/html/rfc2616#section-14.9.1 + def cache_control(*values) + if values.last.kind_of?(Hash) + hash = values.pop + hash.reject! { |k, v| v == false } + hash.reject! { |k, v| values << k if v == true } + else + hash = {} + end + + values.map! { |value| value.to_s.tr('_','-') } + hash.each do |key, value| + key = key.to_s.tr('_', '-') + value = value.to_i if ['max-age', 's-maxage'].include? key + values << "#{key}=#{value}" + end + + response['Cache-Control'] = values.join(', ') if values.any? + end + + # Set the Expires header and Cache-Control/max-age directive. Amount + # can be an integer number of seconds in the future or a Time object + # indicating when the response should be considered "stale". The remaining + # "values" arguments are passed to the #cache_control helper: + # + # expires 500, :public, :must_revalidate + # => Cache-Control: public, must-revalidate, max-age=500 + # => Expires: Mon, 08 Jun 2009 08:50:17 GMT + # + def expires(amount, *values) + values << {} unless values.last.kind_of?(Hash) + + if amount.is_a? Integer + time = Time.now + amount.to_i + max_age = amount + else + time = time_for amount + max_age = time - Time.now + end + + values.last.merge!(:max_age => max_age) + cache_control(*values) + + response['Expires'] = time.httpdate + end + + # Set the last modified time of the resource (HTTP 'Last-Modified' header) + # and halt if conditional GET matches. The +time+ argument is a Time, + # DateTime, or other object that responds to +to_time+. + # + # When the current request includes an 'If-Modified-Since' header that is + # equal or later than the time specified, execution is immediately halted + # with a '304 Not Modified' response. + def last_modified(time) + return unless time + time = time_for time + response['Last-Modified'] = time.httpdate + return if env['HTTP_IF_NONE_MATCH'] + + if status == 200 and env['HTTP_IF_MODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i + halt 304 if since >= time.to_i + end + + if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i + halt 412 if since < time.to_i + end + rescue ArgumentError + end + + ETAG_KINDS = [:strong, :weak] + # Set the response entity tag (HTTP 'ETag' header) and halt if conditional + # GET matches. The +value+ argument is an identifier that uniquely + # identifies the current version of the resource. The +kind+ argument + # indicates whether the etag should be used as a :strong (default) or :weak + # cache validator. + # + # When the current request includes an 'If-None-Match' header with a + # matching etag, execution is immediately halted. If the request method is + # GET or HEAD, a '304 Not Modified' response is sent. + def etag(value, options = {}) + # Before touching this code, please double check RFC 2616 14.24 and 14.26. + options = {:kind => options} unless Hash === options + kind = options[:kind] || :strong + new_resource = options.fetch(:new_resource) { request.post? } + + unless ETAG_KINDS.include?(kind) + raise ArgumentError, ":strong or :weak expected" + end + + value = '"%s"' % value + value = "W/#{value}" if kind == :weak + response['ETag'] = value + + if success? or status == 304 + if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource + halt(request.safe? ? 304 : 412) + end + + if env['HTTP_IF_MATCH'] + halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource + end + end + end + + # Sugar for redirect (example: redirect back) + def back + request.referer + end + + # whether or not the status is set to 1xx + def informational? + status.between? 100, 199 + end + + # whether or not the status is set to 2xx + def success? + status.between? 200, 299 + end + + # whether or not the status is set to 3xx + def redirect? + status.between? 300, 399 + end + + # whether or not the status is set to 4xx + def client_error? + status.between? 400, 499 + end + + # whether or not the status is set to 5xx + def server_error? + status.between? 500, 599 + end + + # whether or not the status is set to 404 + def not_found? + status == 404 + end + + # whether or not the status is set to 400 + def bad_request? + status == 400 + end + + # Generates a Time object from the given value. + # Used by #expires and #last_modified. + def time_for(value) + if value.is_a? Numeric + Time.at value + elsif value.respond_to? :to_s + Time.parse value.to_s + else + value.to_time + end + rescue ArgumentError => boom + raise boom + rescue Exception + raise ArgumentError, "unable to convert #{value.inspect} to a Time object" + end + + private + + # Helper method checking if a ETag value list includes the current ETag. + def etag_matches?(list, new_resource = request.post?) + return !new_resource if list == '*' + list.to_s.split(/\s*,\s*/).include? response['ETag'] + end + + def with_params(temp_params) + original, @params = @params, temp_params + yield + ensure + @params = original if original + end + end + + # Template rendering methods. Each method takes the name of a template + # to render as a Symbol and returns a String with the rendered output, + # as well as an optional hash with additional options. + # + # `template` is either the name or path of the template as symbol + # (Use `:'subdir/myview'` for views in subdirectories), or a string + # that will be rendered. + # + # Possible options are: + # :content_type The content type to use, same arguments as content_type. + # :layout If set to something falsy, no layout is rendered, otherwise + # the specified layout is used (Ignored for `sass` and `less`) + # :layout_engine Engine to use for rendering the layout. + # :locals A hash with local variables that should be available + # in the template + # :scope If set, template is evaluate with the binding of the given + # object rather than the application instance. + # :views Views directory to use. + module Templates + module ContentTyped + attr_accessor :content_type + end + + def initialize + super + @default_layout = :layout + @preferred_extension = nil + end + + def erb(template, options = {}, locals = {}, &block) + render(:erb, template, options, locals, &block) + end + + def erubis(template, options = {}, locals = {}) + warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \ + "If you have Erubis installed, it will be used automatically." + render :erubis, template, options, locals + end + + def haml(template, options = {}, locals = {}, &block) + render(:haml, template, options, locals, &block) + end + + def sass(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :css + render :sass, template, options, locals + end + + def scss(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :css + render :scss, template, options, locals + end + + def less(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :css + render :less, template, options, locals + end + + def stylus(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :css + render :styl, template, options, locals + end + + def builder(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:builder, template, options, locals, &block) + end + + def liquid(template, options = {}, locals = {}, &block) + render(:liquid, template, options, locals, &block) + end + + def markdown(template, options = {}, locals = {}) + options[:exclude_outvar] = true + render :markdown, template, options, locals + end + + def textile(template, options = {}, locals = {}) + render :textile, template, options, locals + end + + def rdoc(template, options = {}, locals = {}) + render :rdoc, template, options, locals + end + + def asciidoc(template, options = {}, locals = {}) + render :asciidoc, template, options, locals + end + + def radius(template, options = {}, locals = {}) + render :radius, template, options, locals + end + + def markaby(template = nil, options = {}, locals = {}, &block) + render_ruby(:mab, template, options, locals, &block) + end + + def coffee(template, options = {}, locals = {}) + options.merge! :layout => false, :default_content_type => :js + render :coffee, template, options, locals + end + + def nokogiri(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:nokogiri, template, options, locals, &block) + end + + def slim(template, options = {}, locals = {}, &block) + render(:slim, template, options, locals, &block) + end + + def creole(template, options = {}, locals = {}) + render :creole, template, options, locals + end + + def mediawiki(template, options = {}, locals = {}) + render :mediawiki, template, options, locals + end + + def wlang(template, options = {}, locals = {}, &block) + render(:wlang, template, options, locals, &block) + end + + def yajl(template, options = {}, locals = {}) + options[:default_content_type] = :json + render :yajl, template, options, locals + end + + def rabl(template, options = {}, locals = {}) + Rabl.register! + render :rabl, template, options, locals + end + + # Calls the given block for every possible template file in views, + # named name.ext, where ext is registered on engine. + def find_template(views, name, engine) + yield ::File.join(views, "#{name}.#{@preferred_extension}") + + Tilt.default_mapping.extensions_for(engine).each do |ext| + yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension + end + end + + private + + # logic shared between builder and nokogiri + def render_ruby(engine, template, options = {}, locals = {}, &block) + options, template = template, nil if template.is_a?(Hash) + template = Proc.new { block } if template.nil? + render engine, template, options, locals + end + + def render(engine, data, options = {}, locals = {}, &block) + # merge app-level options + engine_options = settings.respond_to?(engine) ? settings.send(engine) : {} + options.merge!(engine_options) { |key, v1, v2| v1 } + + # extract generic options + locals = options.delete(:locals) || locals || {} + views = options.delete(:views) || settings.views || "./views" + layout = options[:layout] + layout = false if layout.nil? && options.include?(:layout) + eat_errors = layout.nil? + layout = engine_options[:layout] if layout.nil? or (layout == true && engine_options[:layout] != false) + layout = @default_layout if layout.nil? or layout == true + layout_options = options.delete(:layout_options) || {} + content_type = options.delete(:default_content_type) + content_type = options.delete(:content_type) || content_type + layout_engine = options.delete(:layout_engine) || engine + scope = options.delete(:scope) || self + exclude_outvar = options.delete(:exclude_outvar) + options.delete(:layout) + + # set some defaults + options[:outvar] ||= '@_out_buf' unless exclude_outvar + options[:default_encoding] ||= settings.default_encoding + + # compile and render template + begin + layout_was = @default_layout + @default_layout = false + template = compile_template(engine, data, options, views) + output = template.render(scope, locals, &block) + ensure + @default_layout = layout_was + end + + # render layout + if layout + options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope). + merge!(layout_options) + catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } } + end + + output.extend(ContentTyped).content_type = content_type if content_type + output + end + + def compile_template(engine, data, options, views) + eat_errors = options.delete :eat_errors + template_cache.fetch engine, data, options, views do + template = Tilt[engine] + raise "Template engine not found: #{engine}" if template.nil? + + case data + when Symbol + body, path, line = settings.templates[data] + if body + body = body.call if body.respond_to?(:call) + template.new(path, line.to_i, options) { body } + else + found = false + @preferred_extension = engine.to_s + find_template(views, data, template) do |file| + path ||= file # keep the initial path rather than the last one + if found = File.exist?(file) + path = file + break + end + end + throw :layout_missing if eat_errors and not found + template.new(path, 1, options) + end + when Proc, String + body = data.is_a?(String) ? Proc.new { data } : data + caller = settings.caller_locations.first + path = options[:path] || caller[0] + line = options[:line] || caller[1] + template.new(path, line.to_i, options, &body) + else + raise ArgumentError, "Sorry, don't know how to render #{data.inspect}." + end + end + end + end + + # Base class for all Sinatra applications and middleware. + class Base + include Rack::Utils + include Helpers + include Templates + + URI_INSTANCE = URI::Parser.new + + attr_accessor :app, :env, :request, :response, :params + attr_reader :template_cache + + def initialize(app = nil, **kwargs) + super() + @app = app + @template_cache = Tilt::Cache.new + @pinned_response = nil # whether a before! filter pinned the content-type + yield self if block_given? + end + + # Rack call interface. + def call(env) + dup.call!(env) + end + + def call!(env) # :nodoc: + @env = env + @params = IndifferentHash.new + @request = Request.new(env) + @response = Response.new + @pinned_response = nil + template_cache.clear if settings.reload_templates + + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + + unless @response['Content-Type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif default = settings.default_content_type + content_type default + end + end + + @response.finish + end + + # Access settings defined with Base.set. + def self.settings + self + end + + # Access settings defined with Base.set. + def settings + self.class.settings + end + + def options + warn "Sinatra::Base#options is deprecated and will be removed, " \ + "use #settings instead." + settings + end + + # Exit the current block, halts any further processing + # of the request, and returns the specified response. + def halt(*response) + response = response.first if response.length == 1 + throw :halt, response + end + + # Pass control to the next matching route. + # If there are no more matching routes, Sinatra will + # return a 404 response. + def pass(&block) + throw :pass, block + end + + # Forward the request to the downstream app -- middleware only. + def forward + fail "downstream app not set" unless @app.respond_to? :call + status, headers, body = @app.call env + @response.status = status + @response.body = body + @response.headers.merge! headers + nil + end + + private + + # Run filters defined on the class and all superclasses. + # Accepts an optional block to call after each filter is applied. + def filter!(type, base = settings, &block) + filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters) + base.filters[type].each do |args| + result = process_route(*args) + block.call(result) if block_given? + end + end + + # Run routes defined on the class and all superclasses. + def route!(base = settings, pass_block = nil) + if routes = base.routes[@request.request_method] + routes.each do |pattern, conditions, block| + response.delete_header('Content-Type') unless @pinned_response + + returned_pass_block = process_route(pattern, conditions) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" + route_eval { block[*args] } + end + + # don't wipe out pass_block in superclass + pass_block = returned_pass_block if returned_pass_block + end + end + + # Run routes defined in superclass. + if base.superclass.respond_to?(:routes) + return route!(base.superclass, pass_block) + end + + route_eval(&pass_block) if pass_block + route_missing + end + + # Run a route block and throw :halt with the result. + def route_eval + throw :halt, yield + end + + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, conditions, block = nil, values = []) + route = @request.path_info + route = '/' if route.empty? and not settings.empty_path_info? + route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/') + return unless params = pattern.params(route) + + params.delete("ignore") # TODO: better params handling, maybe turn it into "smart" object or detect changes + force_encoding(params) + @params = @params.merge(params) if params.any? + + regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? {|subpattern| subpattern.is_a?(Mustermann::Regular)} ) + if regexp_exists + captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c } + values += captures + @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty? + else + values += params.values.flatten + end + + catch(:pass) do + conditions.each { |c| throw :pass if c.bind(self).call == false } + block ? block[self, values] : yield(self, values) + end + rescue + @env['sinatra.error.params'] = @params + raise + ensure + params ||= {} + params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params'] + end + + # No matching route was found or all routes passed. The default + # implementation is to forward the request downstream when running + # as middleware (@app is non-nil); when no downstream app is set, raise + # a NotFound exception. Subclasses can override this method to perform + # custom route miss logic. + def route_missing + if @app + forward + else + raise NotFound, "#{request.request_method} #{request.path_info}" + end + end + + # Attempt to serve static files from public directory. Throws :halt when + # a matching file is found, returns nil otherwise. + def static!(options = {}) + return if (public_dir = settings.public_folder).nil? + path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" + return unless valid_path?(path) + + path = File.expand_path(path) + return unless path.start_with?(File.expand_path(public_dir) + '/') + return unless File.file?(path) + + env['sinatra.static_file'] = path + cache_control(*settings.static_cache_control) if settings.static_cache_control? + send_file path, options.merge(:disposition => nil) + end + + # Run the block with 'throw :halt' support and apply result to the response. + def invoke + res = catch(:halt) { yield } + + res = [res] if Integer === res or String === res + if Array === res and Integer === res.first + res = res.dup + status(res.shift) + body(res.pop) + headers(*res) + elsif res.respond_to? :each + body res + end + nil # avoid double setting the same response tuple twice + end + + # Dispatch a request with error handling. + def dispatch! + # Avoid passing frozen string in force_encoding + @params.merge!(@request.params).each do |key, val| + next unless val.respond_to?(:force_encoding) + val = val.dup if val.frozen? + @params[key] = force_encoding(val) + end + + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do + @pinned_response = !response['Content-Type'].nil? + end + route! + end + rescue ::Exception => boom + invoke { handle_exception!(boom) } + ensure + begin + filter! :after unless env['sinatra.static_file'] + rescue ::Exception => boom + invoke { handle_exception!(boom) } unless @env['sinatra.error'] + end + end + + # Error handling during requests. + def handle_exception!(boom) + if error_params = @env['sinatra.error.params'] + @params = @params.merge(error_params) + end + @env['sinatra.error'] = boom + + if boom.respond_to? :http_status and boom.http_status.between? 400, 599 + status(boom.http_status) + elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599 + status(boom.code) + else + status(500) + end + + if server_error? + dump_errors! boom if settings.dump_errors? + raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler + elsif not_found? + headers['X-Cascade'] = 'pass' if settings.x_cascade? + end + + if res = error_block!(boom.class, boom) || error_block!(status, boom) + return res + end + + if not_found? || bad_request? + if boom.message && boom.message != boom.class.name + body Rack::Utils.escape_html(boom.message) + else + content_type 'text/html' + body '

' + (not_found? ? 'Not Found' : 'Bad Request') + '

' + end + end + + return unless server_error? + raise boom if settings.raise_errors? or settings.show_exceptions? + error_block! Exception, boom + end + + # Find an custom error block for the key(s) specified. + def error_block!(key, *block_params) + base = settings + while base.respond_to?(:errors) + next base = base.superclass unless args_array = base.errors[key] + args_array.reverse_each do |args| + first = args == args_array.first + args += [block_params] + resp = process_route(*args) + return resp unless resp.nil? && !first + end + end + return false unless key.respond_to? :superclass and key.superclass < Exception + error_block!(key.superclass, *block_params) + end + + def dump_errors!(boom) + msg = ["#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t") + @env['rack.errors'].puts(msg) + end + + class << self + CALLERS_TO_IGNORE = [ # :nodoc: + /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code + /lib\/tilt.*\.rb$/, # all tilt code + /^\(.*\)$/, # generated code + /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks + /active_support/, # active_support require hacks + /bundler(\/(?:runtime|inline))?\.rb/, # bundler require hacks + /= 1.9.2 + /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files + ] + + # contrary to what the comment said previously, rubinius never supported this + if defined?(RUBY_IGNORE_CALLERS) + warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0" + CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS) + end + + attr_reader :routes, :filters, :templates, :errors + + # Removes all routes, filters, middleware and extension hooks from the + # current class (not routes/filters/... defined by its superclass). + def reset! + @conditions = [] + @routes = {} + @filters = {:before => [], :after => []} + @errors = {} + @middleware = [] + @prototype = nil + @extensions = [] + + if superclass.respond_to?(:templates) + @templates = Hash.new { |hash, key| superclass.templates[key] } + else + @templates = {} + end + end + + # Extension modules registered on this class and all superclasses. + def extensions + if superclass.respond_to?(:extensions) + (@extensions + superclass.extensions).uniq + else + @extensions + end + end + + # Middleware used in this class and all superclasses. + def middleware + if superclass.respond_to?(:middleware) + superclass.middleware + @middleware + else + @middleware + end + end + + # Sets an option to the given value. If the value is a proc, + # the proc will be called every time the option is accessed. + def set(option, value = (not_set = true), ignore_setter = false, &block) + raise ArgumentError if block and !not_set + value, not_set = block, false if block + + if not_set + raise ArgumentError unless option.respond_to?(:each) + option.each { |k,v| set(k, v) } + return self + end + + if respond_to?("#{option}=") and not ignore_setter + return __send__("#{option}=", value) + end + + setter = proc { |val| set option, val, true } + getter = proc { value } + + case value + when Proc + getter = value + when Symbol, Integer, FalseClass, TrueClass, NilClass + getter = value.inspect + when Hash + setter = proc do |val| + val = value.merge val if Hash === val + set option, val, true + end + end + + define_singleton("#{option}=", setter) + define_singleton(option, getter) + define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?" + self + end + + # Same as calling `set :option, true` for each of the given options. + def enable(*opts) + opts.each { |key| set(key, true) } + end + + # Same as calling `set :option, false` for each of the given options. + def disable(*opts) + opts.each { |key| set(key, false) } + end + + # Define a custom error handler. Optionally takes either an Exception + # class, or an HTTP status code to specify which errors should be + # handled. + def error(*codes, &block) + args = compile! "ERROR", /.*/, block + codes = codes.flat_map(&method(:Array)) + codes << Exception if codes.empty? + codes << Sinatra::NotFound if codes.include?(404) + codes.each { |c| (@errors[c] ||= []) << args } + end + + # Sugar for `error(404) { ... }` + def not_found(&block) + error(404, &block) + end + + # Define a named template. The block must return the template source. + def template(name, &block) + filename, line = caller_locations.first + templates[name] = [block, filename, line.to_i] + end + + # Define the layout template. The block must return the template source. + def layout(name = :layout, &block) + template name, &block + end + + # Load embedded templates from the file; uses the caller's __FILE__ + # when no file is specified. + def inline_templates=(file = nil) + file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file + + begin + io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file) + app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2) + rescue Errno::ENOENT + app, data = nil + end + + if data + if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m + encoding = $2 + else + encoding = settings.default_encoding + end + lines = app.count("\n") + 1 + template = nil + force_encoding data, encoding + data.each_line do |line| + lines += 1 + if line =~ /^@@\s*(.*\S)\s*$/ + template = force_encoding(String.new, encoding) + templates[$1.to_sym] = [template, file, lines] + elsif template + template << line + end + end + end + end + + # Lookup or register a mime type in Rack's mime registry. + def mime_type(type, value = nil) + return type if type.nil? + return type.to_s if type.to_s.include?('/') + type = ".#{type}" unless type.to_s[0] == ?. + return Rack::Mime.mime_type(type, nil) unless value + Rack::Mime::MIME_TYPES[type] = value + end + + # provides all mime types matching type, including deprecated types: + # mime_types :html # => ['text/html'] + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type + type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type] + end + + # Define a before filter; runs before all requests within the same + # context as route handlers and may access/modify the request and + # response. + def before(path = /.*/, **options, &block) + add_filter(:before, path, **options, &block) + end + + # Define an after filter; runs after all requests within the same + # context as route handlers and may access/modify the request and + # response. + def after(path = /.*/, **options, &block) + add_filter(:after, path, **options, &block) + end + + # add a filter + def add_filter(type, path = /.*/, **options, &block) + filters[type] << compile!(type, path, block, **options) + end + + # Add a route condition. The route is considered non-matching when the + # block returns false. + def condition(name = "#{caller.first[/`.*'/]} condition", &block) + @conditions << generate_method(name, &block) + end + + def public=(value) + warn ":public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead" + set(:public_folder, value) + end + + def public_dir=(value) + self.public_folder = value + end + + def public_dir + public_folder + end + + # Defining a `GET` handler also automatically defines + # a `HEAD` handler. + def get(path, opts = {}, &block) + conditions = @conditions.dup + route('GET', path, opts, &block) + + @conditions = conditions + route('HEAD', path, opts, &block) + end + + def put(path, opts = {}, &bk) route 'PUT', path, opts, &bk end + def post(path, opts = {}, &bk) route 'POST', path, opts, &bk end + def delete(path, opts = {}, &bk) route 'DELETE', path, opts, &bk end + def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end + def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end + def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end + def link(path, opts = {}, &bk) route 'LINK', path, opts, &bk end + def unlink(path, opts = {}, &bk) route 'UNLINK', path, opts, &bk end + + # Makes the methods defined in the block and in the Modules given + # in `extensions` available to the handlers and templates + def helpers(*extensions, &block) + class_eval(&block) if block_given? + include(*extensions) if extensions.any? + end + + # Register an extension. Alternatively take a block from which an + # extension will be created and registered on the fly. + def register(*extensions, &block) + extensions << Module.new(&block) if block_given? + @extensions += extensions + extensions.each do |extension| + extend extension + extension.registered(self) if extension.respond_to?(:registered) + end + end + + def development?; environment == :development end + def production?; environment == :production end + def test?; environment == :test end + + # Set configuration options for Sinatra and/or the app. + # Allows scoping of settings for certain environments. + def configure(*envs) + yield self if envs.empty? || envs.include?(environment.to_sym) + end + + # Use the specified Rack middleware + def use(middleware, *args, &block) + @prototype = nil + @middleware << [middleware, args, block] + end + ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true) + + # Stop the self-hosted server if running. + def quit! + return unless running? + # Use Thin's hard #stop! if available, otherwise just #stop. + running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop + $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless suppress_messages? + set :running_server, nil + set :handler_name, nil + end + + alias_method :stop!, :quit! + + # Run the Sinatra app as a self-hosted server using + # Puma, Mongrel, or WEBrick (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + return if running? + set options + handler = Rack::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(:Port => port, :Host => bind) + + begin + start_server(handler, server_settings, handler_name, &block) + rescue Errno::EADDRINUSE + $stderr.puts "== Someone is already performing on port #{port}!" + raise + ensure + quit! + end + end + + alias_method :start!, :run! + + # Check whether the self-hosted server is running or not. + def running? + running_server? + end + + # The prototype instance used to process requests. + def prototype + @prototype ||= new + end + + # Create a new instance without middleware in front of it. + alias new! new unless method_defined? :new! + + # Create a new instance of the class fronted by its middleware + # pipeline. The object is guaranteed to respond to #call but may not be + # an instance of the class new was called on. + def new(*args, **kwargs, &bk) + instance = new!(*args, **kwargs, &bk) + Wrapper.new(build(instance).to_app, instance) + end + + # Creates a Rack::Builder instance with all the middleware set up and + # the given +app+ as end point. + def build(app) + builder = Rack::Builder.new + setup_default_middleware builder + setup_middleware builder + builder.run app + builder + end + + def call(env) + synchronize { prototype.call(env) } + end + + # Like Kernel#caller but excluding certain magic entries and without + # line / method information; the resulting array contains filenames only. + def caller_files + cleaned_caller(1).flatten + end + + # Like caller_files, but containing Arrays rather than strings with the + # first element being the file, and the second being the line. + def caller_locations + cleaned_caller 2 + end + + private + + # Starts the server by running the Rack Handler. + def start_server(handler, server_settings, handler_name) + # Ensure we initialize middleware before startup, to match standard Rack + # behavior, by ensuring an instance exists: + prototype + # Run the instance we created: + handler.run(self, **server_settings) do |server| + unless suppress_messages? + $stderr.puts "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}" + end + + setup_traps + set :running_server, server + set :handler_name, handler_name + server.threaded = settings.threaded if server.respond_to? :threaded= + + yield server if block_given? + end + end + + def suppress_messages? + handler_name =~ /cgi/i || quiet + end + + def setup_traps + if traps? + at_exit { quit! } + + [:INT, :TERM].each do |signal| + old_handler = trap(signal) do + quit! + old_handler.call if old_handler.respond_to?(:call) + end + end + + set :traps, false + end + end + + # Dynamically defines a method on settings. + def define_singleton(name, content = Proc.new) + singleton_class.class_eval do + undef_method(name) if method_defined? name + String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content) + end + end + + # Condition for matching host name. Parameter might be String or Regexp. + def host_name(pattern) + condition { pattern === request.host } + end + + # Condition for matching user agent. Parameter should be Regexp. + # Will set params[:agent]. + def user_agent(pattern) + condition do + if request.user_agent.to_s =~ pattern + @params[:agent] = $~[1..-1] + true + else + false + end + end + end + alias_method :agent, :user_agent + + # Condition for matching mimetypes. Accepts file extensions. + def provides(*types) + types.map! { |t| mime_types(t) } + types.flatten! + condition do + if type = response['Content-Type'] + types.include? type or types.include? type[/^[^;]+/] + elsif type = request.preferred_type(types) + params = (type.respond_to?(:params) ? type.params : {}) + content_type(type, params) + true + else + false + end + end + end + + def route(verb, path, options = {}, &block) + enable :empty_path_info if path == "" and empty_path_info.nil? + signature = compile!(verb, path, block, **options) + (@routes[verb] ||= []) << signature + invoke_hook(:route_added, verb, path, block) + signature + end + + def invoke_hook(name, *args) + extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } + end + + def generate_method(method_name, &block) + define_method(method_name, &block) + method = instance_method method_name + remove_method method_name + method + end + + def compile!(verb, path, block, **options) + # Because of self.options.host + host_name(options.delete(:host)) if options.key?(:host) + # Pass Mustermann opts to compile() + route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze + + options.each_pair { |option, args| send(option, *args) } + + pattern = compile(path, route_mustermann_opts) + method_name = "#{verb} #{path}" + unbound_method = generate_method(method_name, &block) + conditions, @conditions = @conditions, [] + wrapper = block.arity != 0 ? + proc { |a, p| unbound_method.bind(a).call(*p) } : + proc { |a, p| unbound_method.bind(a).call } + + [ pattern, conditions, wrapper ] + end + + def compile(path, route_mustermann_opts = {}) + Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts)) + end + + def setup_default_middleware(builder) + builder.use ExtendedRack + builder.use ShowExceptions if show_exceptions? + builder.use Rack::MethodOverride if method_override? + builder.use Rack::Head + setup_logging builder + setup_sessions builder + setup_protection builder + end + + def setup_middleware(builder) + middleware.each { |c,a,b| builder.use(c, *a, &b) } + end + + def setup_logging(builder) + if logging? + setup_common_logger(builder) + setup_custom_logger(builder) + elsif logging == false + setup_null_logger(builder) + end + end + + def setup_null_logger(builder) + builder.use Rack::NullLogger + end + + def setup_common_logger(builder) + builder.use Sinatra::CommonLogger + end + + def setup_custom_logger(builder) + if logging.respond_to? :to_int + builder.use Rack::Logger, logging + else + builder.use Rack::Logger + end + end + + def setup_protection(builder) + return unless protection? + options = Hash === protection ? protection.dup : {} + options = { + img_src: "'self' data:", + font_src: "'self'" + }.merge options + + protect_session = options.fetch(:session) { sessions? } + options[:without_session] = !protect_session + + options[:reaction] ||= :drop_session + + builder.use Rack::Protection, options + end + + def setup_sessions(builder) + return unless sessions? + options = {} + options[:secret] = session_secret if session_secret? + options.merge! sessions.to_hash if sessions.respond_to? :to_hash + builder.use session_store, options + end + + def inherited(subclass) + subclass.reset! + subclass.set :app_file, caller_files.first unless subclass.app_file? + super + end + + @@mutex = Mutex.new + def synchronize(&block) + if lock? + @@mutex.synchronize(&block) + else + yield + end + end + + # used for deprecation warnings + def warn(message) + super message + "\n\tfrom #{cleaned_caller.first.join(':')}" + end + + # Like Kernel#caller but excluding certain magic entries + def cleaned_caller(keep = 3) + caller(1). + map! { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }. + reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } } + end + end + + # Force data to specified encoding. It defaults to settings.default_encoding + # which is UTF-8 by default + def self.force_encoding(data, encoding = default_encoding) + return if data == settings || data.is_a?(Tempfile) + if data.respond_to? :force_encoding + data.force_encoding(encoding).encode! + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v, encoding) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v, encoding) } + end + data + end + + def force_encoding(*args) settings.force_encoding(*args) end + + reset! + + set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym + set :raise_errors, Proc.new { test? } + set :dump_errors, Proc.new { !test? } + set :show_exceptions, Proc.new { development? } + set :sessions, false + set :session_store, Rack::Session::Cookie + set :logging, false + set :protection, true + set :method_override, false + set :use_code, false + set :default_encoding, "utf-8" + set :x_cascade, true + set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" } + settings.add_charset << /^text\// + set :mustermann_opts, {} + set :default_content_type, 'text/html' + + # explicitly generating a session secret eagerly to play nice with preforking + begin + require 'securerandom' + set :session_secret, SecureRandom.hex(64) + rescue LoadError, NotImplementedError + # SecureRandom raises a NotImplementedError if no random device is available + set :session_secret, "%064x" % Kernel.rand(2**256-1) + end + + class << self + alias_method :methodoverride?, :method_override? + alias_method :methodoverride=, :method_override= + end + + set :run, false # start server via at-exit hook? + set :running_server, nil + set :handler_name, nil + set :traps, true + set :server, %w[HTTP webrick] + set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' } + set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567) + set :quiet, false + + ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + + if ruby_engine == 'macruby' + server.unshift 'control_tower' + else + server.unshift 'reel' + server.unshift 'puma' + server.unshift 'mongrel' if ruby_engine.nil? + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + end + + set :absolute_redirects, true + set :prefixed_redirects, false + set :empty_path_info, nil + set :strict_paths, true + + set :app_file, nil + set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) } + set :views, Proc.new { root && File.join(root, 'views') } + set :reload_templates, Proc.new { development? } + set :lock, false + set :threaded, true + + set :public_folder, Proc.new { root && File.join(root, 'public') } + set :static, Proc.new { public_folder && File.exist?(public_folder) } + set :static_cache_control, false + + error ::Exception do + response.status = 500 + content_type 'text/html' + '

Internal Server Error

' + end + + configure :development do + get '/__sinatra__/:image.png' do + filename = __dir__ + "/images/#{params[:image].to_i}.png" + content_type :png + send_file filename + end + + error NotFound do + content_type 'text/html' + + if self.class == Sinatra::Application + code = <<-RUBY.gsub(/^ {12}/, '') + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + RUBY + else + code = <<-RUBY.gsub(/^ {12}/, '') + class #{self.class} + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + end + RUBY + + file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '') + code = "# in #{file}\n#{code}" unless file.empty? + end + + (<<-HTML).gsub(/^ {10}/, '') + + + + + + +

Sinatra doesn’t know this ditty.

+ +
+ Try this: +
#{Rack::Utils.escape_html(code)}
+
+ + + HTML + end + end + end + + # Execution context for classic style (top-level) applications. All + # DSL methods executed on main are delegated to this class. + # + # The Application class should not be subclassed, unless you want to + # inherit all settings, routes, handlers, and error pages from the + # top-level. Subclassing Sinatra::Base is highly recommended for + # modular applications. + class Application < Base + set :logging, Proc.new { !test? } + set :method_override, true + set :run, Proc.new { !test? } + set :app_file, nil + + def self.register(*extensions, &block) #:nodoc: + added_methods = extensions.flat_map(&:public_instance_methods) + Delegator.delegate(*added_methods) + super(*extensions, &block) + end + end + + # Sinatra delegation mixin. Mixing this module into an object causes all + # methods to be delegated to the Sinatra::Application class. Used primarily + # at the top-level. + module Delegator #:nodoc: + def self.delegate(*methods) + methods.each do |method_name| + define_method(method_name) do |*args, &block| + return super(*args, &block) if respond_to? method_name + Delegator.target.send(method_name, *args, &block) + end + # ensure keyword argument passing is compatible with ruby >= 2.7 + ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true) + private method_name + end + end + + delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink, + :template, :layout, :before, :after, :error, :not_found, :configure, + :set, :mime_type, :enable, :disable, :use, :development?, :test?, + :production?, :helpers, :settings, :register + + class << self + attr_accessor :target + end + + self.target = Application + end + + class Wrapper + def initialize(stack, instance) + @stack, @instance = stack, instance + end + + def settings + @instance.settings + end + + def helpers + @instance + end + + def call(env) + @stack.call(env) + end + + def inspect + "#<#{@instance.class} app_file=#{settings.app_file.inspect}>" + end + end + + # Create a new Sinatra application; the block is evaluated in the class scope. + def self.new(base = Base, &block) + base = Class.new(base) + base.class_eval(&block) if block_given? + base + end + + # Extend the top-level DSL with the modules provided. + def self.register(*extensions, &block) + Delegator.target.register(*extensions, &block) + end + + # Include the helper modules provided in Sinatra's request context. + def self.helpers(*extensions, &block) + Delegator.target.helpers(*extensions, &block) + end + + # Use the middleware for classic applications. + def self.use(*args, &block) + Delegator.target.use(*args, &block) + end +end diff --git a/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/expected.json b/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/expected.json new file mode 100644 index 0000000..58ddcc2 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/expected.json @@ -0,0 +1,1065 @@ +{ + "version": "v1", + "actions": [ + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 1 + ], + "type": "string_content", + "label": "\u003ch1[^\u003e]*\u003eHiya(\u003cspan\u003e\u003ca href=", + "start_byte": 343, + "end_byte": 371 + }, + "old_value": "\u003ch1[^\u003e]*\u003eHiya(\u003cspan\u003e\u003ca href=", + "new_value": "\u003ch1[^\u003e]*\u003e(\u003c.*\u003e)?Hiya(\u003c.*\u003e)?\u003c", + "dest_node": { + "tree": "after", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 1 + ], + "type": "string_content", + "label": "\u003ch1[^\u003e]*\u003e(\u003c.*\u003e)?Hiya(\u003c.*\u003e)?\u003c", + "start_byte": 343, + "end_byte": 371 + } + }, + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 1 + ], + "type": "string_content", + "label": "\u003ch1[^\u003e]*\u003eHello From RDoc(\u003cspan\u003e\u003ca href=", + "start_byte": 574, + "end_byte": 613 + }, + "old_value": "\u003ch1[^\u003e]*\u003eHello From RDoc(\u003cspan\u003e\u003ca href=", + "new_value": "\u003ch1[^\u003e]*\u003e(\u003c.*\u003e)?Hello From RDoc(\u003c.*\u003e)?\u003c", + "dest_node": { + "tree": "after", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 1 + ], + "type": "string_content", + "label": "\u003ch1[^\u003e]*\u003e(\u003c.*\u003e)?Hello From RDoc(\u003c.*\u003e)?\u003c", + "start_byte": 491, + "end_byte": 530 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 2 + ], + "type": "escape_sequence", + "label": "\\\"", + "start_byte": 371, + "end_byte": 373 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 3 + ], + "type": "string_content", + "label": "#label-Hiya", + "start_byte": 373, + "end_byte": 384 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 4 + ], + "type": "escape_sequence", + "label": "\\\"", + "start_byte": 384, + "end_byte": 386 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 5 + ], + "type": "string_content", + "label": "\u003e\u0026para;\u003c", + "start_byte": 386, + "end_byte": 394 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 7 + ], + "type": "string_content", + "label": "a\u003e \u003ca href=", + "start_byte": 396, + "end_byte": 407 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 8 + ], + "type": "escape_sequence", + "label": "\\\"", + "start_byte": 407, + "end_byte": 409 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 9 + ], + "type": "string_content", + "label": "#(documentation|top)", + "start_byte": 409, + "end_byte": 429 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 10 + ], + "type": "escape_sequence", + "label": "\\\"", + "start_byte": 429, + "end_byte": 431 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 11 + ], + "type": "string_content", + "label": "\u003e\u0026uarr;\u003c", + "start_byte": 431, + "end_byte": 439 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 12 + ], + "type": "escape_sequence", + "label": "\\/", + "start_byte": 439, + "end_byte": 441 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 13 + ], + "type": "string_content", + "label": "a\u003e\u003c", + "start_byte": 441, + "end_byte": 444 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 14 + ], + "type": "escape_sequence", + "label": "\\/", + "start_byte": 444, + "end_byte": 446 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 15 + ], + "type": "string_content", + "label": "span\u003e)?\u003c", + "start_byte": 446, + "end_byte": 454 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 0, + 16 + ], + "type": "escape_sequence", + "label": "\\/", + "start_byte": 454, + "end_byte": 456 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 2 + ], + "type": "escape_sequence", + "label": "\\\"", + "start_byte": 613, + "end_byte": 615 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 3 + ], + "type": "string_content", + "label": "#label-Hello", + "start_byte": 615, + "end_byte": 627 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 4 + ], + "type": "escape_sequence", + "label": "\\+", + "start_byte": 627, + "end_byte": 629 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 5 + ], + "type": "string_content", + "label": "From", + "start_byte": 629, + "end_byte": 633 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 6 + ], + "type": "escape_sequence", + "label": "\\+", + "start_byte": 633, + "end_byte": 635 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 7 + ], + "type": "string_content", + "label": "RDoc", + "start_byte": 635, + "end_byte": 639 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 8 + ], + "type": "escape_sequence", + "label": "\\\"", + "start_byte": 639, + "end_byte": 641 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 9 + ], + "type": "string_content", + "label": "\u003e\u0026para;\u003c", + "start_byte": 641, + "end_byte": 649 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 11 + ], + "type": "string_content", + "label": "a\u003e \u003ca href=", + "start_byte": 651, + "end_byte": 662 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 12 + ], + "type": "escape_sequence", + "label": "\\\"", + "start_byte": 662, + "end_byte": 664 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 13 + ], + "type": "string_content", + "label": "#(documentation|top)", + "start_byte": 664, + "end_byte": 684 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 14 + ], + "type": "escape_sequence", + "label": "\\\"", + "start_byte": 684, + "end_byte": 686 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 15 + ], + "type": "string_content", + "label": "\u003e\u0026uarr;\u003c", + "start_byte": 686, + "end_byte": 694 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 16 + ], + "type": "escape_sequence", + "label": "\\/", + "start_byte": 694, + "end_byte": 696 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 17 + ], + "type": "string_content", + "label": "a\u003e\u003c", + "start_byte": 696, + "end_byte": 699 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 18 + ], + "type": "escape_sequence", + "label": "\\/", + "start_byte": 699, + "end_byte": 701 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 19 + ], + "type": "string_content", + "label": "span\u003e)?\u003c", + "start_byte": 701, + "end_byte": 709 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 0, + 20 + ], + "type": "escape_sequence", + "label": "\\/", + "start_byte": 709, + "end_byte": 711 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/metadata.json b/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/metadata.json new file mode 100644 index 0000000..dbc6052 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "4062e3669c7dff0f59ca1d0b0bfa67f7eef444af", + "commit": "8b1c32641ff047c56b98b09cc9d7c0e14c142879", + "message": "Fix RDoc 6.16+ compatibility by relaxing the check\n\nWith RDoc 6.16.0, the following test started to fail:", + "author": "VĂ­t Ondruch", + "date": "2025-12-03T15:52:08+01:00", + "language": "ruby", + "files_changed": 1, + "insertions": 2, + "deletions": 2 +} diff --git a/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/new.rb b/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/new.rb new file mode 100644 index 0000000..52ab962 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/new.rb @@ -0,0 +1,66 @@ +require_relative 'test_helper' + +begin +require 'rdoc' +require 'rdoc/markup/to_html' + +class RdocTest < Minitest::Test + def rdoc_app(&block) + mock_app do + set :views, __dir__ + '/views' + get('/', &block) + end + get '/' + end + + it 'renders inline rdoc strings' do + rdoc_app { rdoc '= Hiya' } + assert ok? + assert_body(/]*>(<.*>)?Hiya(<.*>)?<\/h1>/) + end + + it 'renders .rdoc files in views path' do + rdoc_app { rdoc :hello } + assert ok? + assert_body(/]*>(<.*>)?Hello From RDoc(<.*>)?<\/h1>/) + end + + it "raises error if template not found" do + mock_app { get('/') { rdoc :no_such_template } } + assert_raises(Errno::ENOENT) { get('/') } + end + + it "renders with inline layouts" do + mock_app do + layout { 'THIS. IS. #{yield.upcase}!' } + get('/') { rdoc 'Sparta', :layout_engine => :str } + end + get '/' + assert ok? + assert_like 'THIS. IS.

SPARTA

!', body + end + + it "renders with file layouts" do + rdoc_app { + rdoc 'Hello World', :layout => :layout2, :layout_engine => :erb + } + assert ok? + assert_body "ERB Layout!\n

Hello World

" + end + + it "can be used in a nested fashion for partials and whatnot" do + mock_app do + template(:inner) { "hi" } + template(:outer) { "<%= rdoc :inner %>" } + get('/') { erb :outer } + end + + get '/' + assert ok? + assert_like '

hi

', body + end +end + +rescue LoadError + warn "#{$!}: skipping rdoc tests" +end diff --git a/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/old.rb b/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/old.rb new file mode 100644 index 0000000..33e7479 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_rdoc_6_16__compatibility_b_6/old.rb @@ -0,0 +1,66 @@ +require_relative 'test_helper' + +begin +require 'rdoc' +require 'rdoc/markup/to_html' + +class RdocTest < Minitest::Test + def rdoc_app(&block) + mock_app do + set :views, __dir__ + '/views' + get('/', &block) + end + get '/' + end + + it 'renders inline rdoc strings' do + rdoc_app { rdoc '= Hiya' } + assert ok? + assert_body(/]*>Hiya(¶<\/a> ↑<\/a><\/span>)?<\/h1>/) + end + + it 'renders .rdoc files in views path' do + rdoc_app { rdoc :hello } + assert ok? + assert_body(/]*>Hello From RDoc(¶<\/a> ↑<\/a><\/span>)?<\/h1>/) + end + + it "raises error if template not found" do + mock_app { get('/') { rdoc :no_such_template } } + assert_raises(Errno::ENOENT) { get('/') } + end + + it "renders with inline layouts" do + mock_app do + layout { 'THIS. IS. #{yield.upcase}!' } + get('/') { rdoc 'Sparta', :layout_engine => :str } + end + get '/' + assert ok? + assert_like 'THIS. IS.

SPARTA

!', body + end + + it "renders with file layouts" do + rdoc_app { + rdoc 'Hello World', :layout => :layout2, :layout_engine => :erb + } + assert ok? + assert_body "ERB Layout!\n

Hello World

" + end + + it "can be used in a nested fashion for partials and whatnot" do + mock_app do + template(:inner) { "hi" } + template(:outer) { "<%= rdoc :inner %>" } + get('/') { erb :outer } + end + + get '/' + assert ok? + assert_like '

hi

', body + end +end + +rescue LoadError + warn "#{$!}: skipping rdoc tests" +end diff --git a/tests/testdata/ruby_sinatra_fix_redos_12/expected.json b/tests/testdata/ruby_sinatra_fix_redos_12/expected.json new file mode 100644 index 0000000..214df34 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_redos_12/expected.json @@ -0,0 +1,865 @@ +{ + "version": "v1", + "actions": [ + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 1 + ], + "type": "identifier", + "label": "split", + "start_byte": 453, + "end_byte": 458 + }, + "old_value": "split", + "new_value": "map", + "dest_node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 1 + ], + "type": "identifier", + "label": "map", + "start_byte": 465, + "end_byte": 468 + } + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 1 + ], + "type": "logical_operator_literal", + "label": "\u0026\u0026", + "start_byte": 533, + "end_byte": 535 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2 + ], + "type": "binary", + "start_byte": 502, + "end_byte": 573 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 2 + ], + "type": "parenthesized_statements", + "start_byte": 536, + "end_byte": 573 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2 + ], + "type": "binary", + "start_byte": 502, + "end_byte": 573 + }, + "position": 2 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 1 + ], + "type": "logical_operator_literal", + "label": "\u0026\u0026", + "start_byte": 629, + "end_byte": 631 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2 + ], + "type": "binary", + "start_byte": 598, + "end_byte": 669 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 2 + ], + "type": "parenthesized_statements", + "start_byte": 632, + "end_byte": 669 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2 + ], + "type": "binary", + "start_byte": 598, + "end_byte": 669 + }, + "position": 2 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "element_reference", + "start_byte": 425, + "end_byte": 452 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "call", + "start_byte": 426, + "end_byte": 464 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2 + ], + "type": "call", + "start_byte": 425, + "end_byte": 469 + }, + "old_position": 0, + "subtree": true, + "dest_start_byte": 426, + "dest_end_byte": 453, + "group_id": "group-1" + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0, + 1 + ], + "type": "identifier", + "label": "split", + "start_byte": 454, + "end_byte": 459 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "call", + "start_byte": 426, + "end_byte": 464 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0, + 2 + ], + "type": "argument_list", + "start_byte": 459, + "end_byte": 464 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "call", + "start_byte": 426, + "end_byte": 464 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 2, + 0 + ], + "type": "block_argument", + "start_byte": 469, + "end_byte": 476 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 2 + ], + "type": "argument_list", + "start_byte": 468, + "end_byte": 477 + }, + "position": 0, + "subtree": true + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 2, + 1 + ], + "type": "call", + "start_byte": 532, + "end_byte": 566 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 2, + 0 + ], + "type": "unary", + "start_byte": 537, + "end_byte": 572 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 2 + ], + "type": "unary", + "start_byte": 528, + "end_byte": 566 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 538, + "dest_end_byte": 572, + "group_id": "group-1" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 2, + 1 + ], + "type": "call", + "start_byte": 629, + "end_byte": 663 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 2, + 0 + ], + "type": "unary", + "start_byte": 633, + "end_byte": 668 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 2 + ], + "type": "unary", + "start_byte": 625, + "end_byte": 663 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 634, + "dest_end_byte": 668, + "group_id": "group-1" + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 2, + 0, + 0 + ], + "type": "unary_operator_literal", + "label": "!", + "start_byte": 537, + "end_byte": 538 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 2, + 0 + ], + "type": "unary", + "start_byte": 537, + "end_byte": 572 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 2, + 0, + 0 + ], + "type": "unary_operator_literal", + "label": "!", + "start_byte": 633, + "end_byte": 634 + }, + "parent": { + "tree": "after", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 2, + 0 + ], + "type": "unary", + "start_byte": 633, + "end_byte": 668 + }, + "position": 0 + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 2, + 0 + ], + "type": "regex", + "start_byte": 459, + "end_byte": 468 + }, + "subtree": true + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 2, + 0 + ], + "type": "not", + "label": "not", + "start_byte": 528, + "end_byte": 531 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 2, + 2, + 1 + ], + "type": "and", + "label": "and", + "start_byte": 524, + "end_byte": 527 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 2, + 0 + ], + "type": "not", + "label": "not", + "start_byte": 625, + "end_byte": 628 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 3, + 2, + 1 + ], + "type": "and", + "label": "and", + "start_byte": 621, + "end_byte": 624 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": -1, + "right_line": 15 + }, + { + "left_line": 15, + "right_line": 16 + }, + { + "left_line": 16, + "right_line": 17 + }, + { + "left_line": 17, + "right_line": 18 + }, + { + "left_line": -1, + "right_line": 19 + }, + { + "left_line": 18, + "right_line": 20 + }, + { + "left_line": 19, + "right_line": 21 + }, + { + "left_line": 20, + "right_line": 22 + }, + { + "left_line": 21, + "right_line": 23 + }, + { + "left_line": 22, + "right_line": 24 + }, + { + "left_line": 23, + "right_line": 25 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_redos_12/metadata.json b/tests/testdata/ruby_sinatra_fix_redos_12/metadata.json new file mode 100644 index 0000000..ef99598 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_redos_12/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "9031a4449bedee77c59d66d64b4121bb687f2d38", + "commit": "580b271775fe67d8e6bceabc7fd86d82f0f81770", + "message": "fix ReDoS", + "author": "ooooooo_q", + "date": "2022-10-08T11:23:45+09:00", + "language": "ruby", + "files_changed": 1, + "insertions": 5, + "deletions": 3 +} diff --git a/tests/testdata/ruby_sinatra_fix_redos_12/new.rb b/tests/testdata/ruby_sinatra_fix_redos_12/new.rb new file mode 100644 index 0000000..6775f0c --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_redos_12/new.rb @@ -0,0 +1,25 @@ +require 'rack/protection' + +module Rack + module Protection + ## + # Prevented attack:: IP spoofing + # Supported browsers:: all + # More infos:: http://blog.c22.cc/2011/04/22/surveymonkey-ip-spoofing/ + # + # Detect (some) IP spoofing attacks. + class IPSpoofing < Base + default_reaction :deny + + def accepts?(env) + return true unless env.include? 'HTTP_X_FORWARDED_FOR' + + ips = env['HTTP_X_FORWARDED_FOR'].split(',').map(&:strip) + return false if env.include?('HTTP_CLIENT_IP') && (!ips.include? env['HTTP_CLIENT_IP']) + return false if env.include?('HTTP_X_REAL_IP') && (!ips.include? env['HTTP_X_REAL_IP']) + + true + end + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_redos_12/old.rb b/tests/testdata/ruby_sinatra_fix_redos_12/old.rb new file mode 100644 index 0000000..3e404ad --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_redos_12/old.rb @@ -0,0 +1,23 @@ +require 'rack/protection' + +module Rack + module Protection + ## + # Prevented attack:: IP spoofing + # Supported browsers:: all + # More infos:: http://blog.c22.cc/2011/04/22/surveymonkey-ip-spoofing/ + # + # Detect (some) IP spoofing attacks. + class IPSpoofing < Base + default_reaction :deny + + def accepts?(env) + return true unless env.include? 'HTTP_X_FORWARDED_FOR' + ips = env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/) + return false if env.include? 'HTTP_CLIENT_IP' and not ips.include? env['HTTP_CLIENT_IP'] + return false if env.include? 'HTTP_X_REAL_IP' and not ips.include? env['HTTP_X_REAL_IP'] + true + end + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_redos_13/expected.json b/tests/testdata/ruby_sinatra_fix_redos_13/expected.json new file mode 100644 index 0000000..98856e7 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_redos_13/expected.json @@ -0,0 +1,384 @@ +{ + "version": "v1", + "actions": [ + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 1 + ], + "type": "identifier", + "label": "split", + "start_byte": 485, + "end_byte": 490 + }, + "old_value": "split", + "new_value": "map", + "dest_node": { + "tree": "after", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 1 + ], + "type": "identifier", + "label": "map", + "start_byte": 496, + "end_byte": 499 + } + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "element_reference", + "start_byte": 457, + "end_byte": 484 + }, + "parent": { + "tree": "after", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "call", + "start_byte": 457, + "end_byte": 495 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2 + ], + "type": "call", + "start_byte": 457, + "end_byte": 501 + }, + "old_position": 0, + "subtree": true, + "dest_start_byte": 457, + "dest_end_byte": 484 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0, + 1 + ], + "type": "identifier", + "label": "split", + "start_byte": 485, + "end_byte": 490 + }, + "parent": { + "tree": "after", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "call", + "start_byte": 457, + "end_byte": 495 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0, + 2 + ], + "type": "argument_list", + "start_byte": 490, + "end_byte": 495 + }, + "parent": { + "tree": "after", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 0 + ], + "type": "call", + "start_byte": 457, + "end_byte": 495 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 2, + 0 + ], + "type": "block_argument", + "start_byte": 500, + "end_byte": 507 + }, + "parent": { + "tree": "after", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 2 + ], + "type": "argument_list", + "start_byte": 499, + "end_byte": 508 + }, + "position": 0, + "subtree": true + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 2, + 2, + 0, + 8, + 0, + 3, + 1, + 3, + 1, + 2, + 2, + 0 + ], + "type": "regex", + "start_byte": 491, + "end_byte": 500 + }, + "subtree": true + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_redos_13/metadata.json b/tests/testdata/ruby_sinatra_fix_redos_13/metadata.json new file mode 100644 index 0000000..0588d29 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_redos_13/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "ceb49f043c37244bc017c9f33f5fdf18f1662ac7", + "commit": "441c06a7eb1a5d59648806aa773f3a27fa89bc1e", + "message": "fix ReDoS", + "author": "ooooooo_q", + "date": "2022-10-08T11:23:45+09:00", + "language": "ruby", + "files_changed": 1, + "insertions": 1, + "deletions": 1 +} diff --git a/tests/testdata/ruby_sinatra_fix_redos_13/new.rb b/tests/testdata/ruby_sinatra_fix_redos_13/new.rb new file mode 100644 index 0000000..1e6812e --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_redos_13/new.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'rack/protection' + +module Rack + module Protection + ## + # Prevented attack:: IP spoofing + # Supported browsers:: all + # More infos:: http://blog.c22.cc/2011/04/22/surveymonkey-ip-spoofing/ + # + # Detect (some) IP spoofing attacks. + class IPSpoofing < Base + default_reaction :deny + + def accepts?(env) + return true unless env.include? 'HTTP_X_FORWARDED_FOR' + + ips = env['HTTP_X_FORWARDED_FOR'].split(',').map(&:strip) + return false if env.include?('HTTP_CLIENT_IP') && (!ips.include? env['HTTP_CLIENT_IP']) + return false if env.include?('HTTP_X_REAL_IP') && (!ips.include? env['HTTP_X_REAL_IP']) + + true + end + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_redos_13/old.rb b/tests/testdata/ruby_sinatra_fix_redos_13/old.rb new file mode 100644 index 0000000..a879932 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_redos_13/old.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'rack/protection' + +module Rack + module Protection + ## + # Prevented attack:: IP spoofing + # Supported browsers:: all + # More infos:: http://blog.c22.cc/2011/04/22/surveymonkey-ip-spoofing/ + # + # Detect (some) IP spoofing attacks. + class IPSpoofing < Base + default_reaction :deny + + def accepts?(env) + return true unless env.include? 'HTTP_X_FORWARDED_FOR' + + ips = env['HTTP_X_FORWARDED_FOR'].split(/\s*,\s*/) + return false if env.include?('HTTP_CLIENT_IP') && (!ips.include? env['HTTP_CLIENT_IP']) + return false if env.include?('HTTP_X_REAL_IP') && (!ips.include? env['HTTP_X_REAL_IP']) + + true + end + end + end +end diff --git a/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/expected.json b/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/expected.json new file mode 100644 index 0000000..950f0b0 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/expected.json @@ -0,0 +1,8932 @@ +{ + "version": "v1", + "actions": [ + { + "action": "update", + "node": { + "tree": "before", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 1 + ], + "type": "identifier", + "label": "split", + "start_byte": 20965, + "end_byte": 20970 + }, + "old_value": "split", + "new_value": "map", + "dest_node": { + "tree": "after", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 1 + ], + "type": "identifier", + "label": "map", + "start_byte": 20976, + "end_byte": 20979 + } + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 0 + ], + "type": "call", + "start_byte": 20955, + "end_byte": 20964 + }, + "parent": { + "tree": "after", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 0 + ], + "type": "call", + "start_byte": 20955, + "end_byte": 20975 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0 + ], + "type": "call", + "start_byte": 20955, + "end_byte": 20981 + }, + "old_position": 0, + "subtree": true, + "dest_start_byte": 20955, + "dest_end_byte": 20964 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 0, + 1 + ], + "type": "identifier", + "label": "split", + "start_byte": 20965, + "end_byte": 20970 + }, + "parent": { + "tree": "after", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 0 + ], + "type": "call", + "start_byte": 20955, + "end_byte": 20975 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 0, + 2 + ], + "type": "argument_list", + "start_byte": 20970, + "end_byte": 20975 + }, + "parent": { + "tree": "after", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 0 + ], + "type": "call", + "start_byte": 20955, + "end_byte": 20975 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 2, + 0 + ], + "type": "block_argument", + "start_byte": 20980, + "end_byte": 20987 + }, + "parent": { + "tree": "after", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 2 + ], + "type": "argument_list", + "start_byte": 20979, + "end_byte": 20988 + }, + "position": 0, + "subtree": true + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 19, + 4, + 19, + 3, + 109, + 3, + 1, + 0, + 2, + 0 + ], + "type": "regex", + "start_byte": 20971, + "end_byte": 20980 + }, + "subtree": true + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": 277, + "right_line": 277 + }, + { + "left_line": 278, + "right_line": 278 + }, + { + "left_line": 279, + "right_line": 279 + }, + { + "left_line": 280, + "right_line": 280 + }, + { + "left_line": 281, + "right_line": 281 + }, + { + "left_line": 282, + "right_line": 282 + }, + { + "left_line": 283, + "right_line": 283 + }, + { + "left_line": 284, + "right_line": 284 + }, + { + "left_line": 285, + "right_line": 285 + }, + { + "left_line": 286, + "right_line": 286 + }, + { + "left_line": 287, + "right_line": 287 + }, + { + "left_line": 288, + "right_line": 288 + }, + { + "left_line": 289, + "right_line": 289 + }, + { + "left_line": 290, + "right_line": 290 + }, + { + "left_line": 291, + "right_line": 291 + }, + { + "left_line": 292, + "right_line": 292 + }, + { + "left_line": 293, + "right_line": 293 + }, + { + "left_line": 294, + "right_line": 294 + }, + { + "left_line": 295, + "right_line": 295 + }, + { + "left_line": 296, + "right_line": 296 + }, + { + "left_line": 297, + "right_line": 297 + }, + { + "left_line": 298, + "right_line": 298 + }, + { + "left_line": 299, + "right_line": 299 + }, + { + "left_line": 300, + "right_line": 300 + }, + { + "left_line": 301, + "right_line": 301 + }, + { + "left_line": 302, + "right_line": 302 + }, + { + "left_line": 303, + "right_line": 303 + }, + { + "left_line": 304, + "right_line": 304 + }, + { + "left_line": 305, + "right_line": 305 + }, + { + "left_line": 306, + "right_line": 306 + }, + { + "left_line": 307, + "right_line": 307 + }, + { + "left_line": 308, + "right_line": 308 + }, + { + "left_line": 309, + "right_line": 309 + }, + { + "left_line": 310, + "right_line": 310 + }, + { + "left_line": 311, + "right_line": 311 + }, + { + "left_line": 312, + "right_line": 312 + }, + { + "left_line": 313, + "right_line": 313 + }, + { + "left_line": 314, + "right_line": 314 + }, + { + "left_line": 315, + "right_line": 315 + }, + { + "left_line": 316, + "right_line": 316 + }, + { + "left_line": 317, + "right_line": 317 + }, + { + "left_line": 318, + "right_line": 318 + }, + { + "left_line": 319, + "right_line": 319 + }, + { + "left_line": 320, + "right_line": 320 + }, + { + "left_line": 321, + "right_line": 321 + }, + { + "left_line": 322, + "right_line": 322 + }, + { + "left_line": 323, + "right_line": 323 + }, + { + "left_line": 324, + "right_line": 324 + }, + { + "left_line": 325, + "right_line": 325 + }, + { + "left_line": 326, + "right_line": 326 + }, + { + "left_line": 327, + "right_line": 327 + }, + { + "left_line": 328, + "right_line": 328 + }, + { + "left_line": 329, + "right_line": 329 + }, + { + "left_line": 330, + "right_line": 330 + }, + { + "left_line": 331, + "right_line": 331 + }, + { + "left_line": 332, + "right_line": 332 + }, + { + "left_line": 333, + "right_line": 333 + }, + { + "left_line": 334, + "right_line": 334 + }, + { + "left_line": 335, + "right_line": 335 + }, + { + "left_line": 336, + "right_line": 336 + }, + { + "left_line": 337, + "right_line": 337 + }, + { + "left_line": 338, + "right_line": 338 + }, + { + "left_line": 339, + "right_line": 339 + }, + { + "left_line": 340, + "right_line": 340 + }, + { + "left_line": 341, + "right_line": 341 + }, + { + "left_line": 342, + "right_line": 342 + }, + { + "left_line": 343, + "right_line": 343 + }, + { + "left_line": 344, + "right_line": 344 + }, + { + "left_line": 345, + "right_line": 345 + }, + { + "left_line": 346, + "right_line": 346 + }, + { + "left_line": 347, + "right_line": 347 + }, + { + "left_line": 348, + "right_line": 348 + }, + { + "left_line": 349, + "right_line": 349 + }, + { + "left_line": 350, + "right_line": 350 + }, + { + "left_line": 351, + "right_line": 351 + }, + { + "left_line": 352, + "right_line": 352 + }, + { + "left_line": 353, + "right_line": 353 + }, + { + "left_line": 354, + "right_line": 354 + }, + { + "left_line": 355, + "right_line": 355 + }, + { + "left_line": 356, + "right_line": 356 + }, + { + "left_line": 357, + "right_line": 357 + }, + { + "left_line": 358, + "right_line": 358 + }, + { + "left_line": 359, + "right_line": 359 + }, + { + "left_line": 360, + "right_line": 360 + }, + { + "left_line": 361, + "right_line": 361 + }, + { + "left_line": 362, + "right_line": 362 + }, + { + "left_line": 363, + "right_line": 363 + }, + { + "left_line": 364, + "right_line": 364 + }, + { + "left_line": 365, + "right_line": 365 + }, + { + "left_line": 366, + "right_line": 366 + }, + { + "left_line": 367, + "right_line": 367 + }, + { + "left_line": 368, + "right_line": 368 + }, + { + "left_line": 369, + "right_line": 369 + }, + { + "left_line": 370, + "right_line": 370 + }, + { + "left_line": 371, + "right_line": 371 + }, + { + "left_line": 372, + "right_line": 372 + }, + { + "left_line": 373, + "right_line": 373 + }, + { + "left_line": 374, + "right_line": 374 + }, + { + "left_line": 375, + "right_line": 375 + }, + { + "left_line": 376, + "right_line": 376 + }, + { + "left_line": 377, + "right_line": 377 + }, + { + "left_line": 378, + "right_line": 378 + }, + { + "left_line": 379, + "right_line": 379 + }, + { + "left_line": 380, + "right_line": 380 + }, + { + "left_line": 381, + "right_line": 381 + }, + { + "left_line": 382, + "right_line": 382 + }, + { + "left_line": 383, + "right_line": 383 + }, + { + "left_line": 384, + "right_line": 384 + }, + { + "left_line": 385, + "right_line": 385 + }, + { + "left_line": 386, + "right_line": 386 + }, + { + "left_line": 387, + "right_line": 387 + }, + { + "left_line": 388, + "right_line": 388 + }, + { + "left_line": 389, + "right_line": 389 + }, + { + "left_line": 390, + "right_line": 390 + }, + { + "left_line": 391, + "right_line": 391 + }, + { + "left_line": 392, + "right_line": 392 + }, + { + "left_line": 393, + "right_line": 393 + }, + { + "left_line": 394, + "right_line": 394 + }, + { + "left_line": 395, + "right_line": 395 + }, + { + "left_line": 396, + "right_line": 396 + }, + { + "left_line": 397, + "right_line": 397 + }, + { + "left_line": 398, + "right_line": 398 + }, + { + "left_line": 399, + "right_line": 399 + }, + { + "left_line": 400, + "right_line": 400 + }, + { + "left_line": 401, + "right_line": 401 + }, + { + "left_line": 402, + "right_line": 402 + }, + { + "left_line": 403, + "right_line": 403 + }, + { + "left_line": 404, + "right_line": 404 + }, + { + "left_line": 405, + "right_line": 405 + }, + { + "left_line": 406, + "right_line": 406 + }, + { + "left_line": 407, + "right_line": 407 + }, + { + "left_line": 408, + "right_line": 408 + }, + { + "left_line": 409, + "right_line": 409 + }, + { + "left_line": 410, + "right_line": 410 + }, + { + "left_line": 411, + "right_line": 411 + }, + { + "left_line": 412, + "right_line": 412 + }, + { + "left_line": 413, + "right_line": 413 + }, + { + "left_line": 414, + "right_line": 414 + }, + { + "left_line": 415, + "right_line": 415 + }, + { + "left_line": 416, + "right_line": 416 + }, + { + "left_line": 417, + "right_line": 417 + }, + { + "left_line": 418, + "right_line": 418 + }, + { + "left_line": 419, + "right_line": 419 + }, + { + "left_line": 420, + "right_line": 420 + }, + { + "left_line": 421, + "right_line": 421 + }, + { + "left_line": 422, + "right_line": 422 + }, + { + "left_line": 423, + "right_line": 423 + }, + { + "left_line": 424, + "right_line": 424 + }, + { + "left_line": 425, + "right_line": 425 + }, + { + "left_line": 426, + "right_line": 426 + }, + { + "left_line": 427, + "right_line": 427 + }, + { + "left_line": 428, + "right_line": 428 + }, + { + "left_line": 429, + "right_line": 429 + }, + { + "left_line": 430, + "right_line": 430 + }, + { + "left_line": 431, + "right_line": 431 + }, + { + "left_line": 432, + "right_line": 432 + }, + { + "left_line": 433, + "right_line": 433 + }, + { + "left_line": 434, + "right_line": 434 + }, + { + "left_line": 435, + "right_line": 435 + }, + { + "left_line": 436, + "right_line": 436 + }, + { + "left_line": 437, + "right_line": 437 + }, + { + "left_line": 438, + "right_line": 438 + }, + { + "left_line": 439, + "right_line": 439 + }, + { + "left_line": 440, + "right_line": 440 + }, + { + "left_line": 441, + "right_line": 441 + }, + { + "left_line": 442, + "right_line": 442 + }, + { + "left_line": 443, + "right_line": 443 + }, + { + "left_line": 444, + "right_line": 444 + }, + { + "left_line": 445, + "right_line": 445 + }, + { + "left_line": 446, + "right_line": 446 + }, + { + "left_line": 447, + "right_line": 447 + }, + { + "left_line": 448, + "right_line": 448 + }, + { + "left_line": 449, + "right_line": 449 + }, + { + "left_line": 450, + "right_line": 450 + }, + { + "left_line": 451, + "right_line": 451 + }, + { + "left_line": 452, + "right_line": 452 + }, + { + "left_line": 453, + "right_line": 453 + }, + { + "left_line": 454, + "right_line": 454 + }, + { + "left_line": 455, + "right_line": 455 + }, + { + "left_line": 456, + "right_line": 456 + }, + { + "left_line": 457, + "right_line": 457 + }, + { + "left_line": 458, + "right_line": 458 + }, + { + "left_line": 459, + "right_line": 459 + }, + { + "left_line": 460, + "right_line": 460 + }, + { + "left_line": 461, + "right_line": 461 + }, + { + "left_line": 462, + "right_line": 462 + }, + { + "left_line": 463, + "right_line": 463 + }, + { + "left_line": 464, + "right_line": 464 + }, + { + "left_line": 465, + "right_line": 465 + }, + { + "left_line": 466, + "right_line": 466 + }, + { + "left_line": 467, + "right_line": 467 + }, + { + "left_line": 468, + "right_line": 468 + }, + { + "left_line": 469, + "right_line": 469 + }, + { + "left_line": 470, + "right_line": 470 + }, + { + "left_line": 471, + "right_line": 471 + }, + { + "left_line": 472, + "right_line": 472 + }, + { + "left_line": 473, + "right_line": 473 + }, + { + "left_line": 474, + "right_line": 474 + }, + { + "left_line": 475, + "right_line": 475 + }, + { + "left_line": 476, + "right_line": 476 + }, + { + "left_line": 477, + "right_line": 477 + }, + { + "left_line": 478, + "right_line": 478 + }, + { + "left_line": 479, + "right_line": 479 + }, + { + "left_line": 480, + "right_line": 480 + }, + { + "left_line": 481, + "right_line": 481 + }, + { + "left_line": 482, + "right_line": 482 + }, + { + "left_line": 483, + "right_line": 483 + }, + { + "left_line": 484, + "right_line": 484 + }, + { + "left_line": 485, + "right_line": 485 + }, + { + "left_line": 486, + "right_line": 486 + }, + { + "left_line": 487, + "right_line": 487 + }, + { + "left_line": 488, + "right_line": 488 + }, + { + "left_line": 489, + "right_line": 489 + }, + { + "left_line": 490, + "right_line": 490 + }, + { + "left_line": 491, + "right_line": 491 + }, + { + "left_line": 492, + "right_line": 492 + }, + { + "left_line": 493, + "right_line": 493 + }, + { + "left_line": 494, + "right_line": 494 + }, + { + "left_line": 495, + "right_line": 495 + }, + { + "left_line": 496, + "right_line": 496 + }, + { + "left_line": 497, + "right_line": 497 + }, + { + "left_line": 498, + "right_line": 498 + }, + { + "left_line": 499, + "right_line": 499 + }, + { + "left_line": 500, + "right_line": 500 + }, + { + "left_line": 501, + "right_line": 501 + }, + { + "left_line": 502, + "right_line": 502 + }, + { + "left_line": 503, + "right_line": 503 + }, + { + "left_line": 504, + "right_line": 504 + }, + { + "left_line": 505, + "right_line": 505 + }, + { + "left_line": 506, + "right_line": 506 + }, + { + "left_line": 507, + "right_line": 507 + }, + { + "left_line": 508, + "right_line": 508 + }, + { + "left_line": 509, + "right_line": 509 + }, + { + "left_line": 510, + "right_line": 510 + }, + { + "left_line": 511, + "right_line": 511 + }, + { + "left_line": 512, + "right_line": 512 + }, + { + "left_line": 513, + "right_line": 513 + }, + { + "left_line": 514, + "right_line": 514 + }, + { + "left_line": 515, + "right_line": 515 + }, + { + "left_line": 516, + "right_line": 516 + }, + { + "left_line": 517, + "right_line": 517 + }, + { + "left_line": 518, + "right_line": 518 + }, + { + "left_line": 519, + "right_line": 519 + }, + { + "left_line": 520, + "right_line": 520 + }, + { + "left_line": 521, + "right_line": 521 + }, + { + "left_line": 522, + "right_line": 522 + }, + { + "left_line": 523, + "right_line": 523 + }, + { + "left_line": 524, + "right_line": 524 + }, + { + "left_line": 525, + "right_line": 525 + }, + { + "left_line": 526, + "right_line": 526 + }, + { + "left_line": 527, + "right_line": 527 + }, + { + "left_line": 528, + "right_line": 528 + }, + { + "left_line": 529, + "right_line": 529 + }, + { + "left_line": 530, + "right_line": 530 + }, + { + "left_line": 531, + "right_line": 531 + }, + { + "left_line": 532, + "right_line": 532 + }, + { + "left_line": 533, + "right_line": 533 + }, + { + "left_line": 534, + "right_line": 534 + }, + { + "left_line": 535, + "right_line": 535 + }, + { + "left_line": 536, + "right_line": 536 + }, + { + "left_line": 537, + "right_line": 537 + }, + { + "left_line": 538, + "right_line": 538 + }, + { + "left_line": 539, + "right_line": 539 + }, + { + "left_line": 540, + "right_line": 540 + }, + { + "left_line": 541, + "right_line": 541 + }, + { + "left_line": 542, + "right_line": 542 + }, + { + "left_line": 543, + "right_line": 543 + }, + { + "left_line": 544, + "right_line": 544 + }, + { + "left_line": 545, + "right_line": 545 + }, + { + "left_line": 546, + "right_line": 546 + }, + { + "left_line": 547, + "right_line": 547 + }, + { + "left_line": 548, + "right_line": 548 + }, + { + "left_line": 549, + "right_line": 549 + }, + { + "left_line": 550, + "right_line": 550 + }, + { + "left_line": 551, + "right_line": 551 + }, + { + "left_line": 552, + "right_line": 552 + }, + { + "left_line": 553, + "right_line": 553 + }, + { + "left_line": 554, + "right_line": 554 + }, + { + "left_line": 555, + "right_line": 555 + }, + { + "left_line": 556, + "right_line": 556 + }, + { + "left_line": 557, + "right_line": 557 + }, + { + "left_line": 558, + "right_line": 558 + }, + { + "left_line": 559, + "right_line": 559 + }, + { + "left_line": 560, + "right_line": 560 + }, + { + "left_line": 561, + "right_line": 561 + }, + { + "left_line": 562, + "right_line": 562 + }, + { + "left_line": 563, + "right_line": 563 + }, + { + "left_line": 564, + "right_line": 564 + }, + { + "left_line": 565, + "right_line": 565 + }, + { + "left_line": 566, + "right_line": 566 + }, + { + "left_line": 567, + "right_line": 567 + }, + { + "left_line": 568, + "right_line": 568 + }, + { + "left_line": 569, + "right_line": 569 + }, + { + "left_line": 570, + "right_line": 570 + }, + { + "left_line": 571, + "right_line": 571 + }, + { + "left_line": 572, + "right_line": 572 + }, + { + "left_line": 573, + "right_line": 573 + }, + { + "left_line": 574, + "right_line": 574 + }, + { + "left_line": 575, + "right_line": 575 + }, + { + "left_line": 576, + "right_line": 576 + }, + { + "left_line": 577, + "right_line": 577 + }, + { + "left_line": 578, + "right_line": 578 + }, + { + "left_line": 579, + "right_line": 579 + }, + { + "left_line": 580, + "right_line": 580 + }, + { + "left_line": 581, + "right_line": 581 + }, + { + "left_line": 582, + "right_line": 582 + }, + { + "left_line": 583, + "right_line": 583 + }, + { + "left_line": 584, + "right_line": 584 + }, + { + "left_line": 585, + "right_line": 585 + }, + { + "left_line": 586, + "right_line": 586 + }, + { + "left_line": 587, + "right_line": 587 + }, + { + "left_line": 588, + "right_line": 588 + }, + { + "left_line": 589, + "right_line": 589 + }, + { + "left_line": 590, + "right_line": 590 + }, + { + "left_line": 591, + "right_line": 591 + }, + { + "left_line": 592, + "right_line": 592 + }, + { + "left_line": 593, + "right_line": 593 + }, + { + "left_line": 594, + "right_line": 594 + }, + { + "left_line": 595, + "right_line": 595 + }, + { + "left_line": 596, + "right_line": 596 + }, + { + "left_line": 597, + "right_line": 597 + }, + { + "left_line": 598, + "right_line": 598 + }, + { + "left_line": 599, + "right_line": 599 + }, + { + "left_line": 600, + "right_line": 600 + }, + { + "left_line": 601, + "right_line": 601 + }, + { + "left_line": 602, + "right_line": 602 + }, + { + "left_line": 603, + "right_line": 603 + }, + { + "left_line": 604, + "right_line": 604 + }, + { + "left_line": 605, + "right_line": 605 + }, + { + "left_line": 606, + "right_line": 606 + }, + { + "left_line": 607, + "right_line": 607 + }, + { + "left_line": 608, + "right_line": 608 + }, + { + "left_line": 609, + "right_line": 609 + }, + { + "left_line": 610, + "right_line": 610 + }, + { + "left_line": 611, + "right_line": 611 + }, + { + "left_line": 612, + "right_line": 612 + }, + { + "left_line": 613, + "right_line": 613 + }, + { + "left_line": 614, + "right_line": 614 + }, + { + "left_line": 615, + "right_line": 615 + }, + { + "left_line": 616, + "right_line": 616 + }, + { + "left_line": 617, + "right_line": 617 + }, + { + "left_line": 618, + "right_line": 618 + }, + { + "left_line": 619, + "right_line": 619 + }, + { + "left_line": 620, + "right_line": 620 + }, + { + "left_line": 621, + "right_line": 621 + }, + { + "left_line": 622, + "right_line": 622 + }, + { + "left_line": 623, + "right_line": 623 + }, + { + "left_line": 624, + "right_line": 624 + }, + { + "left_line": 625, + "right_line": 625 + }, + { + "left_line": 626, + "right_line": 626 + }, + { + "left_line": 627, + "right_line": 627 + }, + { + "left_line": 628, + "right_line": 628 + }, + { + "left_line": 629, + "right_line": 629 + }, + { + "left_line": 630, + "right_line": 630 + }, + { + "left_line": 631, + "right_line": 631 + }, + { + "left_line": 632, + "right_line": 632 + }, + { + "left_line": 633, + "right_line": 633 + }, + { + "left_line": 634, + "right_line": 634 + }, + { + "left_line": 635, + "right_line": 635 + }, + { + "left_line": 636, + "right_line": 636 + }, + { + "left_line": 637, + "right_line": 637 + }, + { + "left_line": 638, + "right_line": 638 + }, + { + "left_line": 639, + "right_line": 639 + }, + { + "left_line": 640, + "right_line": 640 + }, + { + "left_line": 641, + "right_line": 641 + }, + { + "left_line": 642, + "right_line": 642 + }, + { + "left_line": 643, + "right_line": 643 + }, + { + "left_line": 644, + "right_line": 644 + }, + { + "left_line": 645, + "right_line": 645 + }, + { + "left_line": 646, + "right_line": 646 + }, + { + "left_line": 647, + "right_line": 647 + }, + { + "left_line": 648, + "right_line": 648 + }, + { + "left_line": 649, + "right_line": 649 + }, + { + "left_line": 650, + "right_line": 650 + }, + { + "left_line": 651, + "right_line": 651 + }, + { + "left_line": 652, + "right_line": 652 + }, + { + "left_line": 653, + "right_line": 653 + }, + { + "left_line": 654, + "right_line": 654 + }, + { + "left_line": 655, + "right_line": 655 + }, + { + "left_line": 656, + "right_line": 656 + }, + { + "left_line": 657, + "right_line": 657 + }, + { + "left_line": 658, + "right_line": 658 + }, + { + "left_line": 659, + "right_line": 659 + }, + { + "left_line": 660, + "right_line": 660 + }, + { + "left_line": 661, + "right_line": 661 + }, + { + "left_line": 662, + "right_line": 662 + }, + { + "left_line": 663, + "right_line": 663 + }, + { + "left_line": 664, + "right_line": 664 + }, + { + "left_line": 665, + "right_line": 665 + }, + { + "left_line": 666, + "right_line": 666 + }, + { + "left_line": 667, + "right_line": 667 + }, + { + "left_line": 668, + "right_line": 668 + }, + { + "left_line": 669, + "right_line": 669 + }, + { + "left_line": 670, + "right_line": 670 + }, + { + "left_line": 671, + "right_line": 671 + }, + { + "left_line": 672, + "right_line": 672 + }, + { + "left_line": 673, + "right_line": 673 + }, + { + "left_line": 674, + "right_line": 674 + }, + { + "left_line": 675, + "right_line": 675 + }, + { + "left_line": 676, + "right_line": 676 + }, + { + "left_line": 677, + "right_line": 677 + }, + { + "left_line": 678, + "right_line": 678 + }, + { + "left_line": 679, + "right_line": 679 + }, + { + "left_line": 680, + "right_line": 680 + }, + { + "left_line": 681, + "right_line": 681 + }, + { + "left_line": 682, + "right_line": 682 + }, + { + "left_line": 683, + "right_line": 683 + }, + { + "left_line": 684, + "right_line": 684 + }, + { + "left_line": 685, + "right_line": 685 + }, + { + "left_line": 686, + "right_line": 686 + }, + { + "left_line": 687, + "right_line": 687 + }, + { + "left_line": 688, + "right_line": 688 + }, + { + "left_line": 689, + "right_line": 689 + }, + { + "left_line": 690, + "right_line": 690 + }, + { + "left_line": 691, + "right_line": 691 + }, + { + "left_line": 692, + "right_line": 692 + }, + { + "left_line": 693, + "right_line": 693 + }, + { + "left_line": 694, + "right_line": 694 + }, + { + "left_line": 695, + "right_line": 695 + }, + { + "left_line": 696, + "right_line": 696 + }, + { + "left_line": 697, + "right_line": 697 + }, + { + "left_line": 698, + "right_line": 698 + }, + { + "left_line": 699, + "right_line": 699 + }, + { + "left_line": 700, + "right_line": 700 + }, + { + "left_line": 701, + "right_line": 701 + }, + { + "left_line": 702, + "right_line": 702 + }, + { + "left_line": 703, + "right_line": 703 + }, + { + "left_line": 704, + "right_line": 704 + }, + { + "left_line": 705, + "right_line": 705 + }, + { + "left_line": 706, + "right_line": 706 + }, + { + "left_line": 707, + "right_line": 707 + }, + { + "left_line": 708, + "right_line": 708 + }, + { + "left_line": 709, + "right_line": 709 + }, + { + "left_line": 710, + "right_line": 710 + }, + { + "left_line": 711, + "right_line": 711 + }, + { + "left_line": 712, + "right_line": 712 + }, + { + "left_line": 713, + "right_line": 713 + }, + { + "left_line": 714, + "right_line": 714 + }, + { + "left_line": 715, + "right_line": 715 + }, + { + "left_line": 716, + "right_line": 716 + }, + { + "left_line": 717, + "right_line": 717 + }, + { + "left_line": 718, + "right_line": 718 + }, + { + "left_line": 719, + "right_line": 719 + }, + { + "left_line": 720, + "right_line": 720 + }, + { + "left_line": 721, + "right_line": 721 + }, + { + "left_line": 722, + "right_line": 722 + }, + { + "left_line": 723, + "right_line": 723 + }, + { + "left_line": 724, + "right_line": 724 + }, + { + "left_line": 725, + "right_line": 725 + }, + { + "left_line": 726, + "right_line": 726 + }, + { + "left_line": 727, + "right_line": 727 + }, + { + "left_line": 728, + "right_line": 728 + }, + { + "left_line": 729, + "right_line": 729 + }, + { + "left_line": 730, + "right_line": 730 + }, + { + "left_line": 731, + "right_line": 731 + }, + { + "left_line": 732, + "right_line": 732 + }, + { + "left_line": 733, + "right_line": 733 + }, + { + "left_line": 734, + "right_line": 734 + }, + { + "left_line": 735, + "right_line": 735 + }, + { + "left_line": 736, + "right_line": 736 + }, + { + "left_line": 737, + "right_line": 737 + }, + { + "left_line": 738, + "right_line": 738 + }, + { + "left_line": 739, + "right_line": 739 + }, + { + "left_line": 740, + "right_line": 740 + }, + { + "left_line": 741, + "right_line": 741 + }, + { + "left_line": 742, + "right_line": 742 + }, + { + "left_line": 743, + "right_line": 743 + }, + { + "left_line": 744, + "right_line": 744 + }, + { + "left_line": 745, + "right_line": 745 + }, + { + "left_line": 746, + "right_line": 746 + }, + { + "left_line": 747, + "right_line": 747 + }, + { + "left_line": 748, + "right_line": 748 + }, + { + "left_line": 749, + "right_line": 749 + }, + { + "left_line": 750, + "right_line": 750 + }, + { + "left_line": 751, + "right_line": 751 + }, + { + "left_line": 752, + "right_line": 752 + }, + { + "left_line": 753, + "right_line": 753 + }, + { + "left_line": 754, + "right_line": 754 + }, + { + "left_line": 755, + "right_line": 755 + }, + { + "left_line": 756, + "right_line": 756 + }, + { + "left_line": 757, + "right_line": 757 + }, + { + "left_line": 758, + "right_line": 758 + }, + { + "left_line": 759, + "right_line": 759 + }, + { + "left_line": 760, + "right_line": 760 + }, + { + "left_line": 761, + "right_line": 761 + }, + { + "left_line": 762, + "right_line": 762 + }, + { + "left_line": 763, + "right_line": 763 + }, + { + "left_line": 764, + "right_line": 764 + }, + { + "left_line": 765, + "right_line": 765 + }, + { + "left_line": 766, + "right_line": 766 + }, + { + "left_line": 767, + "right_line": 767 + }, + { + "left_line": 768, + "right_line": 768 + }, + { + "left_line": 769, + "right_line": 769 + }, + { + "left_line": 770, + "right_line": 770 + }, + { + "left_line": 771, + "right_line": 771 + }, + { + "left_line": 772, + "right_line": 772 + }, + { + "left_line": 773, + "right_line": 773 + }, + { + "left_line": 774, + "right_line": 774 + }, + { + "left_line": 775, + "right_line": 775 + }, + { + "left_line": 776, + "right_line": 776 + }, + { + "left_line": 777, + "right_line": 777 + }, + { + "left_line": 778, + "right_line": 778 + }, + { + "left_line": 779, + "right_line": 779 + }, + { + "left_line": 780, + "right_line": 780 + }, + { + "left_line": 781, + "right_line": 781 + }, + { + "left_line": 782, + "right_line": 782 + }, + { + "left_line": 783, + "right_line": 783 + }, + { + "left_line": 784, + "right_line": 784 + }, + { + "left_line": 785, + "right_line": 785 + }, + { + "left_line": 786, + "right_line": 786 + }, + { + "left_line": 787, + "right_line": 787 + }, + { + "left_line": 788, + "right_line": 788 + }, + { + "left_line": 789, + "right_line": 789 + }, + { + "left_line": 790, + "right_line": 790 + }, + { + "left_line": 791, + "right_line": 791 + }, + { + "left_line": 792, + "right_line": 792 + }, + { + "left_line": 793, + "right_line": 793 + }, + { + "left_line": 794, + "right_line": 794 + }, + { + "left_line": 795, + "right_line": 795 + }, + { + "left_line": 796, + "right_line": 796 + }, + { + "left_line": 797, + "right_line": 797 + }, + { + "left_line": 798, + "right_line": 798 + }, + { + "left_line": 799, + "right_line": 799 + }, + { + "left_line": 800, + "right_line": 800 + }, + { + "left_line": 801, + "right_line": 801 + }, + { + "left_line": 802, + "right_line": 802 + }, + { + "left_line": 803, + "right_line": 803 + }, + { + "left_line": 804, + "right_line": 804 + }, + { + "left_line": 805, + "right_line": 805 + }, + { + "left_line": 806, + "right_line": 806 + }, + { + "left_line": 807, + "right_line": 807 + }, + { + "left_line": 808, + "right_line": 808 + }, + { + "left_line": 809, + "right_line": 809 + }, + { + "left_line": 810, + "right_line": 810 + }, + { + "left_line": 811, + "right_line": 811 + }, + { + "left_line": 812, + "right_line": 812 + }, + { + "left_line": 813, + "right_line": 813 + }, + { + "left_line": 814, + "right_line": 814 + }, + { + "left_line": 815, + "right_line": 815 + }, + { + "left_line": 816, + "right_line": 816 + }, + { + "left_line": 817, + "right_line": 817 + }, + { + "left_line": 818, + "right_line": 818 + }, + { + "left_line": 819, + "right_line": 819 + }, + { + "left_line": 820, + "right_line": 820 + }, + { + "left_line": 821, + "right_line": 821 + }, + { + "left_line": 822, + "right_line": 822 + }, + { + "left_line": 823, + "right_line": 823 + }, + { + "left_line": 824, + "right_line": 824 + }, + { + "left_line": 825, + "right_line": 825 + }, + { + "left_line": 826, + "right_line": 826 + }, + { + "left_line": 827, + "right_line": 827 + }, + { + "left_line": 828, + "right_line": 828 + }, + { + "left_line": 829, + "right_line": 829 + }, + { + "left_line": 830, + "right_line": 830 + }, + { + "left_line": 831, + "right_line": 831 + }, + { + "left_line": 832, + "right_line": 832 + }, + { + "left_line": 833, + "right_line": 833 + }, + { + "left_line": 834, + "right_line": 834 + }, + { + "left_line": 835, + "right_line": 835 + }, + { + "left_line": 836, + "right_line": 836 + }, + { + "left_line": 837, + "right_line": 837 + }, + { + "left_line": 838, + "right_line": 838 + }, + { + "left_line": 839, + "right_line": 839 + }, + { + "left_line": 840, + "right_line": 840 + }, + { + "left_line": 841, + "right_line": 841 + }, + { + "left_line": 842, + "right_line": 842 + }, + { + "left_line": 843, + "right_line": 843 + }, + { + "left_line": 844, + "right_line": 844 + }, + { + "left_line": 845, + "right_line": 845 + }, + { + "left_line": 846, + "right_line": 846 + }, + { + "left_line": 847, + "right_line": 847 + }, + { + "left_line": 848, + "right_line": 848 + }, + { + "left_line": 849, + "right_line": 849 + }, + { + "left_line": 850, + "right_line": 850 + }, + { + "left_line": 851, + "right_line": 851 + }, + { + "left_line": 852, + "right_line": 852 + }, + { + "left_line": 853, + "right_line": 853 + }, + { + "left_line": 854, + "right_line": 854 + }, + { + "left_line": 855, + "right_line": 855 + }, + { + "left_line": 856, + "right_line": 856 + }, + { + "left_line": 857, + "right_line": 857 + }, + { + "left_line": 858, + "right_line": 858 + }, + { + "left_line": 859, + "right_line": 859 + }, + { + "left_line": 860, + "right_line": 860 + }, + { + "left_line": 861, + "right_line": 861 + }, + { + "left_line": 862, + "right_line": 862 + }, + { + "left_line": 863, + "right_line": 863 + }, + { + "left_line": 864, + "right_line": 864 + }, + { + "left_line": 865, + "right_line": 865 + }, + { + "left_line": 866, + "right_line": 866 + }, + { + "left_line": 867, + "right_line": 867 + }, + { + "left_line": 868, + "right_line": 868 + }, + { + "left_line": 869, + "right_line": 869 + }, + { + "left_line": 870, + "right_line": 870 + }, + { + "left_line": 871, + "right_line": 871 + }, + { + "left_line": 872, + "right_line": 872 + }, + { + "left_line": 873, + "right_line": 873 + }, + { + "left_line": 874, + "right_line": 874 + }, + { + "left_line": 875, + "right_line": 875 + }, + { + "left_line": 876, + "right_line": 876 + }, + { + "left_line": 877, + "right_line": 877 + }, + { + "left_line": 878, + "right_line": 878 + }, + { + "left_line": 879, + "right_line": 879 + }, + { + "left_line": 880, + "right_line": 880 + }, + { + "left_line": 881, + "right_line": 881 + }, + { + "left_line": 882, + "right_line": 882 + }, + { + "left_line": 883, + "right_line": 883 + }, + { + "left_line": 884, + "right_line": 884 + }, + { + "left_line": 885, + "right_line": 885 + }, + { + "left_line": 886, + "right_line": 886 + }, + { + "left_line": 887, + "right_line": 887 + }, + { + "left_line": 888, + "right_line": 888 + }, + { + "left_line": 889, + "right_line": 889 + }, + { + "left_line": 890, + "right_line": 890 + }, + { + "left_line": 891, + "right_line": 891 + }, + { + "left_line": 892, + "right_line": 892 + }, + { + "left_line": 893, + "right_line": 893 + }, + { + "left_line": 894, + "right_line": 894 + }, + { + "left_line": 895, + "right_line": 895 + }, + { + "left_line": 896, + "right_line": 896 + }, + { + "left_line": 897, + "right_line": 897 + }, + { + "left_line": 898, + "right_line": 898 + }, + { + "left_line": 899, + "right_line": 899 + }, + { + "left_line": 900, + "right_line": 900 + }, + { + "left_line": 901, + "right_line": 901 + }, + { + "left_line": 902, + "right_line": 902 + }, + { + "left_line": 903, + "right_line": 903 + }, + { + "left_line": 904, + "right_line": 904 + }, + { + "left_line": 905, + "right_line": 905 + }, + { + "left_line": 906, + "right_line": 906 + }, + { + "left_line": 907, + "right_line": 907 + }, + { + "left_line": 908, + "right_line": 908 + }, + { + "left_line": 909, + "right_line": 909 + }, + { + "left_line": 910, + "right_line": 910 + }, + { + "left_line": 911, + "right_line": 911 + }, + { + "left_line": 912, + "right_line": 912 + }, + { + "left_line": 913, + "right_line": 913 + }, + { + "left_line": 914, + "right_line": 914 + }, + { + "left_line": 915, + "right_line": 915 + }, + { + "left_line": 916, + "right_line": 916 + }, + { + "left_line": 917, + "right_line": 917 + }, + { + "left_line": 918, + "right_line": 918 + }, + { + "left_line": 919, + "right_line": 919 + }, + { + "left_line": 920, + "right_line": 920 + }, + { + "left_line": 921, + "right_line": 921 + }, + { + "left_line": 922, + "right_line": 922 + }, + { + "left_line": 923, + "right_line": 923 + }, + { + "left_line": 924, + "right_line": 924 + }, + { + "left_line": 925, + "right_line": 925 + }, + { + "left_line": 926, + "right_line": 926 + }, + { + "left_line": 927, + "right_line": 927 + }, + { + "left_line": 928, + "right_line": 928 + }, + { + "left_line": 929, + "right_line": 929 + }, + { + "left_line": 930, + "right_line": 930 + }, + { + "left_line": 931, + "right_line": 931 + }, + { + "left_line": 932, + "right_line": 932 + }, + { + "left_line": 933, + "right_line": 933 + }, + { + "left_line": 934, + "right_line": 934 + }, + { + "left_line": 935, + "right_line": 935 + }, + { + "left_line": 936, + "right_line": 936 + }, + { + "left_line": 937, + "right_line": 937 + }, + { + "left_line": 938, + "right_line": 938 + }, + { + "left_line": 939, + "right_line": 939 + }, + { + "left_line": 940, + "right_line": 940 + }, + { + "left_line": 941, + "right_line": 941 + }, + { + "left_line": 942, + "right_line": 942 + }, + { + "left_line": 943, + "right_line": 943 + }, + { + "left_line": 944, + "right_line": 944 + }, + { + "left_line": 945, + "right_line": 945 + }, + { + "left_line": 946, + "right_line": 946 + }, + { + "left_line": 947, + "right_line": 947 + }, + { + "left_line": 948, + "right_line": 948 + }, + { + "left_line": 949, + "right_line": 949 + }, + { + "left_line": 950, + "right_line": 950 + }, + { + "left_line": 951, + "right_line": 951 + }, + { + "left_line": 952, + "right_line": 952 + }, + { + "left_line": 953, + "right_line": 953 + }, + { + "left_line": 954, + "right_line": 954 + }, + { + "left_line": 955, + "right_line": 955 + }, + { + "left_line": 956, + "right_line": 956 + }, + { + "left_line": 957, + "right_line": 957 + }, + { + "left_line": 958, + "right_line": 958 + }, + { + "left_line": 959, + "right_line": 959 + }, + { + "left_line": 960, + "right_line": 960 + }, + { + "left_line": 961, + "right_line": 961 + }, + { + "left_line": 962, + "right_line": 962 + }, + { + "left_line": 963, + "right_line": 963 + }, + { + "left_line": 964, + "right_line": 964 + }, + { + "left_line": 965, + "right_line": 965 + }, + { + "left_line": 966, + "right_line": 966 + }, + { + "left_line": 967, + "right_line": 967 + }, + { + "left_line": 968, + "right_line": 968 + }, + { + "left_line": 969, + "right_line": 969 + }, + { + "left_line": 970, + "right_line": 970 + }, + { + "left_line": 971, + "right_line": 971 + }, + { + "left_line": 972, + "right_line": 972 + }, + { + "left_line": 973, + "right_line": 973 + }, + { + "left_line": 974, + "right_line": 974 + }, + { + "left_line": 975, + "right_line": 975 + }, + { + "left_line": 976, + "right_line": 976 + }, + { + "left_line": 977, + "right_line": 977 + }, + { + "left_line": 978, + "right_line": 978 + }, + { + "left_line": 979, + "right_line": 979 + }, + { + "left_line": 980, + "right_line": 980 + }, + { + "left_line": 981, + "right_line": 981 + }, + { + "left_line": 982, + "right_line": 982 + }, + { + "left_line": 983, + "right_line": 983 + }, + { + "left_line": 984, + "right_line": 984 + }, + { + "left_line": 985, + "right_line": 985 + }, + { + "left_line": 986, + "right_line": 986 + }, + { + "left_line": 987, + "right_line": 987 + }, + { + "left_line": 988, + "right_line": 988 + }, + { + "left_line": 989, + "right_line": 989 + }, + { + "left_line": 990, + "right_line": 990 + }, + { + "left_line": 991, + "right_line": 991 + }, + { + "left_line": 992, + "right_line": 992 + }, + { + "left_line": 993, + "right_line": 993 + }, + { + "left_line": 994, + "right_line": 994 + }, + { + "left_line": 995, + "right_line": 995 + }, + { + "left_line": 996, + "right_line": 996 + }, + { + "left_line": 997, + "right_line": 997 + }, + { + "left_line": 998, + "right_line": 998 + }, + { + "left_line": 999, + "right_line": 999 + }, + { + "left_line": 1000, + "right_line": 1000 + }, + { + "left_line": 1001, + "right_line": 1001 + }, + { + "left_line": 1002, + "right_line": 1002 + }, + { + "left_line": 1003, + "right_line": 1003 + }, + { + "left_line": 1004, + "right_line": 1004 + }, + { + "left_line": 1005, + "right_line": 1005 + }, + { + "left_line": 1006, + "right_line": 1006 + }, + { + "left_line": 1007, + "right_line": 1007 + }, + { + "left_line": 1008, + "right_line": 1008 + }, + { + "left_line": 1009, + "right_line": 1009 + }, + { + "left_line": 1010, + "right_line": 1010 + }, + { + "left_line": 1011, + "right_line": 1011 + }, + { + "left_line": 1012, + "right_line": 1012 + }, + { + "left_line": 1013, + "right_line": 1013 + }, + { + "left_line": 1014, + "right_line": 1014 + }, + { + "left_line": 1015, + "right_line": 1015 + }, + { + "left_line": 1016, + "right_line": 1016 + }, + { + "left_line": 1017, + "right_line": 1017 + }, + { + "left_line": 1018, + "right_line": 1018 + }, + { + "left_line": 1019, + "right_line": 1019 + }, + { + "left_line": 1020, + "right_line": 1020 + }, + { + "left_line": 1021, + "right_line": 1021 + }, + { + "left_line": 1022, + "right_line": 1022 + }, + { + "left_line": 1023, + "right_line": 1023 + }, + { + "left_line": 1024, + "right_line": 1024 + }, + { + "left_line": 1025, + "right_line": 1025 + }, + { + "left_line": 1026, + "right_line": 1026 + }, + { + "left_line": 1027, + "right_line": 1027 + }, + { + "left_line": 1028, + "right_line": 1028 + }, + { + "left_line": 1029, + "right_line": 1029 + }, + { + "left_line": 1030, + "right_line": 1030 + }, + { + "left_line": 1031, + "right_line": 1031 + }, + { + "left_line": 1032, + "right_line": 1032 + }, + { + "left_line": 1033, + "right_line": 1033 + }, + { + "left_line": 1034, + "right_line": 1034 + }, + { + "left_line": 1035, + "right_line": 1035 + }, + { + "left_line": 1036, + "right_line": 1036 + }, + { + "left_line": 1037, + "right_line": 1037 + }, + { + "left_line": 1038, + "right_line": 1038 + }, + { + "left_line": 1039, + "right_line": 1039 + }, + { + "left_line": 1040, + "right_line": 1040 + }, + { + "left_line": 1041, + "right_line": 1041 + }, + { + "left_line": 1042, + "right_line": 1042 + }, + { + "left_line": 1043, + "right_line": 1043 + }, + { + "left_line": 1044, + "right_line": 1044 + }, + { + "left_line": 1045, + "right_line": 1045 + }, + { + "left_line": 1046, + "right_line": 1046 + }, + { + "left_line": 1047, + "right_line": 1047 + }, + { + "left_line": 1048, + "right_line": 1048 + }, + { + "left_line": 1049, + "right_line": 1049 + }, + { + "left_line": 1050, + "right_line": 1050 + }, + { + "left_line": 1051, + "right_line": 1051 + }, + { + "left_line": 1052, + "right_line": 1052 + }, + { + "left_line": 1053, + "right_line": 1053 + }, + { + "left_line": 1054, + "right_line": 1054 + }, + { + "left_line": 1055, + "right_line": 1055 + }, + { + "left_line": 1056, + "right_line": 1056 + }, + { + "left_line": 1057, + "right_line": 1057 + }, + { + "left_line": 1058, + "right_line": 1058 + }, + { + "left_line": 1059, + "right_line": 1059 + }, + { + "left_line": 1060, + "right_line": 1060 + }, + { + "left_line": 1061, + "right_line": 1061 + }, + { + "left_line": 1062, + "right_line": 1062 + }, + { + "left_line": 1063, + "right_line": 1063 + }, + { + "left_line": 1064, + "right_line": 1064 + }, + { + "left_line": 1065, + "right_line": 1065 + }, + { + "left_line": 1066, + "right_line": 1066 + }, + { + "left_line": 1067, + "right_line": 1067 + }, + { + "left_line": 1068, + "right_line": 1068 + }, + { + "left_line": 1069, + "right_line": 1069 + }, + { + "left_line": 1070, + "right_line": 1070 + }, + { + "left_line": 1071, + "right_line": 1071 + }, + { + "left_line": 1072, + "right_line": 1072 + }, + { + "left_line": 1073, + "right_line": 1073 + }, + { + "left_line": 1074, + "right_line": 1074 + }, + { + "left_line": 1075, + "right_line": 1075 + }, + { + "left_line": 1076, + "right_line": 1076 + }, + { + "left_line": 1077, + "right_line": 1077 + }, + { + "left_line": 1078, + "right_line": 1078 + }, + { + "left_line": 1079, + "right_line": 1079 + }, + { + "left_line": 1080, + "right_line": 1080 + }, + { + "left_line": 1081, + "right_line": 1081 + }, + { + "left_line": 1082, + "right_line": 1082 + }, + { + "left_line": 1083, + "right_line": 1083 + }, + { + "left_line": 1084, + "right_line": 1084 + }, + { + "left_line": 1085, + "right_line": 1085 + }, + { + "left_line": 1086, + "right_line": 1086 + }, + { + "left_line": 1087, + "right_line": 1087 + }, + { + "left_line": 1088, + "right_line": 1088 + }, + { + "left_line": 1089, + "right_line": 1089 + }, + { + "left_line": 1090, + "right_line": 1090 + }, + { + "left_line": 1091, + "right_line": 1091 + }, + { + "left_line": 1092, + "right_line": 1092 + }, + { + "left_line": 1093, + "right_line": 1093 + }, + { + "left_line": 1094, + "right_line": 1094 + }, + { + "left_line": 1095, + "right_line": 1095 + }, + { + "left_line": 1096, + "right_line": 1096 + }, + { + "left_line": 1097, + "right_line": 1097 + }, + { + "left_line": 1098, + "right_line": 1098 + }, + { + "left_line": 1099, + "right_line": 1099 + }, + { + "left_line": 1100, + "right_line": 1100 + }, + { + "left_line": 1101, + "right_line": 1101 + }, + { + "left_line": 1102, + "right_line": 1102 + }, + { + "left_line": 1103, + "right_line": 1103 + }, + { + "left_line": 1104, + "right_line": 1104 + }, + { + "left_line": 1105, + "right_line": 1105 + }, + { + "left_line": 1106, + "right_line": 1106 + }, + { + "left_line": 1107, + "right_line": 1107 + }, + { + "left_line": 1108, + "right_line": 1108 + }, + { + "left_line": 1109, + "right_line": 1109 + }, + { + "left_line": 1110, + "right_line": 1110 + }, + { + "left_line": 1111, + "right_line": 1111 + }, + { + "left_line": 1112, + "right_line": 1112 + }, + { + "left_line": 1113, + "right_line": 1113 + }, + { + "left_line": 1114, + "right_line": 1114 + }, + { + "left_line": 1115, + "right_line": 1115 + }, + { + "left_line": 1116, + "right_line": 1116 + }, + { + "left_line": 1117, + "right_line": 1117 + }, + { + "left_line": 1118, + "right_line": 1118 + }, + { + "left_line": 1119, + "right_line": 1119 + }, + { + "left_line": 1120, + "right_line": 1120 + }, + { + "left_line": 1121, + "right_line": 1121 + }, + { + "left_line": 1122, + "right_line": 1122 + }, + { + "left_line": 1123, + "right_line": 1123 + }, + { + "left_line": 1124, + "right_line": 1124 + }, + { + "left_line": 1125, + "right_line": 1125 + }, + { + "left_line": 1126, + "right_line": 1126 + }, + { + "left_line": 1127, + "right_line": 1127 + }, + { + "left_line": 1128, + "right_line": 1128 + }, + { + "left_line": 1129, + "right_line": 1129 + }, + { + "left_line": 1130, + "right_line": 1130 + }, + { + "left_line": 1131, + "right_line": 1131 + }, + { + "left_line": 1132, + "right_line": 1132 + }, + { + "left_line": 1133, + "right_line": 1133 + }, + { + "left_line": 1134, + "right_line": 1134 + }, + { + "left_line": 1135, + "right_line": 1135 + }, + { + "left_line": 1136, + "right_line": 1136 + }, + { + "left_line": 1137, + "right_line": 1137 + }, + { + "left_line": 1138, + "right_line": 1138 + }, + { + "left_line": 1139, + "right_line": 1139 + }, + { + "left_line": 1140, + "right_line": 1140 + }, + { + "left_line": 1141, + "right_line": 1141 + }, + { + "left_line": 1142, + "right_line": 1142 + }, + { + "left_line": 1143, + "right_line": 1143 + }, + { + "left_line": 1144, + "right_line": 1144 + }, + { + "left_line": 1145, + "right_line": 1145 + }, + { + "left_line": 1146, + "right_line": 1146 + }, + { + "left_line": 1147, + "right_line": 1147 + }, + { + "left_line": 1148, + "right_line": 1148 + }, + { + "left_line": 1149, + "right_line": 1149 + }, + { + "left_line": 1150, + "right_line": 1150 + }, + { + "left_line": 1151, + "right_line": 1151 + }, + { + "left_line": 1152, + "right_line": 1152 + }, + { + "left_line": 1153, + "right_line": 1153 + }, + { + "left_line": 1154, + "right_line": 1154 + }, + { + "left_line": 1155, + "right_line": 1155 + }, + { + "left_line": 1156, + "right_line": 1156 + }, + { + "left_line": 1157, + "right_line": 1157 + }, + { + "left_line": 1158, + "right_line": 1158 + }, + { + "left_line": 1159, + "right_line": 1159 + }, + { + "left_line": 1160, + "right_line": 1160 + }, + { + "left_line": 1161, + "right_line": 1161 + }, + { + "left_line": 1162, + "right_line": 1162 + }, + { + "left_line": 1163, + "right_line": 1163 + }, + { + "left_line": 1164, + "right_line": 1164 + }, + { + "left_line": 1165, + "right_line": 1165 + }, + { + "left_line": 1166, + "right_line": 1166 + }, + { + "left_line": 1167, + "right_line": 1167 + }, + { + "left_line": 1168, + "right_line": 1168 + }, + { + "left_line": 1169, + "right_line": 1169 + }, + { + "left_line": 1170, + "right_line": 1170 + }, + { + "left_line": 1171, + "right_line": 1171 + }, + { + "left_line": 1172, + "right_line": 1172 + }, + { + "left_line": 1173, + "right_line": 1173 + }, + { + "left_line": 1174, + "right_line": 1174 + }, + { + "left_line": 1175, + "right_line": 1175 + }, + { + "left_line": 1176, + "right_line": 1176 + }, + { + "left_line": 1177, + "right_line": 1177 + }, + { + "left_line": 1178, + "right_line": 1178 + }, + { + "left_line": 1179, + "right_line": 1179 + }, + { + "left_line": 1180, + "right_line": 1180 + }, + { + "left_line": 1181, + "right_line": 1181 + }, + { + "left_line": 1182, + "right_line": 1182 + }, + { + "left_line": 1183, + "right_line": 1183 + }, + { + "left_line": 1184, + "right_line": 1184 + }, + { + "left_line": 1185, + "right_line": 1185 + }, + { + "left_line": 1186, + "right_line": 1186 + }, + { + "left_line": 1187, + "right_line": 1187 + }, + { + "left_line": 1188, + "right_line": 1188 + }, + { + "left_line": 1189, + "right_line": 1189 + }, + { + "left_line": 1190, + "right_line": 1190 + }, + { + "left_line": 1191, + "right_line": 1191 + }, + { + "left_line": 1192, + "right_line": 1192 + }, + { + "left_line": 1193, + "right_line": 1193 + }, + { + "left_line": 1194, + "right_line": 1194 + }, + { + "left_line": 1195, + "right_line": 1195 + }, + { + "left_line": 1196, + "right_line": 1196 + }, + { + "left_line": 1197, + "right_line": 1197 + }, + { + "left_line": 1198, + "right_line": 1198 + }, + { + "left_line": 1199, + "right_line": 1199 + }, + { + "left_line": 1200, + "right_line": 1200 + }, + { + "left_line": 1201, + "right_line": 1201 + }, + { + "left_line": 1202, + "right_line": 1202 + }, + { + "left_line": 1203, + "right_line": 1203 + }, + { + "left_line": 1204, + "right_line": 1204 + }, + { + "left_line": 1205, + "right_line": 1205 + }, + { + "left_line": 1206, + "right_line": 1206 + }, + { + "left_line": 1207, + "right_line": 1207 + }, + { + "left_line": 1208, + "right_line": 1208 + }, + { + "left_line": 1209, + "right_line": 1209 + }, + { + "left_line": 1210, + "right_line": 1210 + }, + { + "left_line": 1211, + "right_line": 1211 + }, + { + "left_line": 1212, + "right_line": 1212 + }, + { + "left_line": 1213, + "right_line": 1213 + }, + { + "left_line": 1214, + "right_line": 1214 + }, + { + "left_line": 1215, + "right_line": 1215 + }, + { + "left_line": 1216, + "right_line": 1216 + }, + { + "left_line": 1217, + "right_line": 1217 + }, + { + "left_line": 1218, + "right_line": 1218 + }, + { + "left_line": 1219, + "right_line": 1219 + }, + { + "left_line": 1220, + "right_line": 1220 + }, + { + "left_line": 1221, + "right_line": 1221 + }, + { + "left_line": 1222, + "right_line": 1222 + }, + { + "left_line": 1223, + "right_line": 1223 + }, + { + "left_line": 1224, + "right_line": 1224 + }, + { + "left_line": 1225, + "right_line": 1225 + }, + { + "left_line": 1226, + "right_line": 1226 + }, + { + "left_line": 1227, + "right_line": 1227 + }, + { + "left_line": 1228, + "right_line": 1228 + }, + { + "left_line": 1229, + "right_line": 1229 + }, + { + "left_line": 1230, + "right_line": 1230 + }, + { + "left_line": 1231, + "right_line": 1231 + }, + { + "left_line": 1232, + "right_line": 1232 + }, + { + "left_line": 1233, + "right_line": 1233 + }, + { + "left_line": 1234, + "right_line": 1234 + }, + { + "left_line": 1235, + "right_line": 1235 + }, + { + "left_line": 1236, + "right_line": 1236 + }, + { + "left_line": 1237, + "right_line": 1237 + }, + { + "left_line": 1238, + "right_line": 1238 + }, + { + "left_line": 1239, + "right_line": 1239 + }, + { + "left_line": 1240, + "right_line": 1240 + }, + { + "left_line": 1241, + "right_line": 1241 + }, + { + "left_line": 1242, + "right_line": 1242 + }, + { + "left_line": 1243, + "right_line": 1243 + }, + { + "left_line": 1244, + "right_line": 1244 + }, + { + "left_line": 1245, + "right_line": 1245 + }, + { + "left_line": 1246, + "right_line": 1246 + }, + { + "left_line": 1247, + "right_line": 1247 + }, + { + "left_line": 1248, + "right_line": 1248 + }, + { + "left_line": 1249, + "right_line": 1249 + }, + { + "left_line": 1250, + "right_line": 1250 + }, + { + "left_line": 1251, + "right_line": 1251 + }, + { + "left_line": 1252, + "right_line": 1252 + }, + { + "left_line": 1253, + "right_line": 1253 + }, + { + "left_line": 1254, + "right_line": 1254 + }, + { + "left_line": 1255, + "right_line": 1255 + }, + { + "left_line": 1256, + "right_line": 1256 + }, + { + "left_line": 1257, + "right_line": 1257 + }, + { + "left_line": 1258, + "right_line": 1258 + }, + { + "left_line": 1259, + "right_line": 1259 + }, + { + "left_line": 1260, + "right_line": 1260 + }, + { + "left_line": 1261, + "right_line": 1261 + }, + { + "left_line": 1262, + "right_line": 1262 + }, + { + "left_line": 1263, + "right_line": 1263 + }, + { + "left_line": 1264, + "right_line": 1264 + }, + { + "left_line": 1265, + "right_line": 1265 + }, + { + "left_line": 1266, + "right_line": 1266 + }, + { + "left_line": 1267, + "right_line": 1267 + }, + { + "left_line": 1268, + "right_line": 1268 + }, + { + "left_line": 1269, + "right_line": 1269 + }, + { + "left_line": 1270, + "right_line": 1270 + }, + { + "left_line": 1271, + "right_line": 1271 + }, + { + "left_line": 1272, + "right_line": 1272 + }, + { + "left_line": 1273, + "right_line": 1273 + }, + { + "left_line": 1274, + "right_line": 1274 + }, + { + "left_line": 1275, + "right_line": 1275 + }, + { + "left_line": 1276, + "right_line": 1276 + }, + { + "left_line": 1277, + "right_line": 1277 + }, + { + "left_line": 1278, + "right_line": 1278 + }, + { + "left_line": 1279, + "right_line": 1279 + }, + { + "left_line": 1280, + "right_line": 1280 + }, + { + "left_line": 1281, + "right_line": 1281 + }, + { + "left_line": 1282, + "right_line": 1282 + }, + { + "left_line": 1283, + "right_line": 1283 + }, + { + "left_line": 1284, + "right_line": 1284 + }, + { + "left_line": 1285, + "right_line": 1285 + }, + { + "left_line": 1286, + "right_line": 1286 + }, + { + "left_line": 1287, + "right_line": 1287 + }, + { + "left_line": 1288, + "right_line": 1288 + }, + { + "left_line": 1289, + "right_line": 1289 + }, + { + "left_line": 1290, + "right_line": 1290 + }, + { + "left_line": 1291, + "right_line": 1291 + }, + { + "left_line": 1292, + "right_line": 1292 + }, + { + "left_line": 1293, + "right_line": 1293 + }, + { + "left_line": 1294, + "right_line": 1294 + }, + { + "left_line": 1295, + "right_line": 1295 + }, + { + "left_line": 1296, + "right_line": 1296 + }, + { + "left_line": 1297, + "right_line": 1297 + }, + { + "left_line": 1298, + "right_line": 1298 + }, + { + "left_line": 1299, + "right_line": 1299 + }, + { + "left_line": 1300, + "right_line": 1300 + }, + { + "left_line": 1301, + "right_line": 1301 + }, + { + "left_line": 1302, + "right_line": 1302 + }, + { + "left_line": 1303, + "right_line": 1303 + }, + { + "left_line": 1304, + "right_line": 1304 + }, + { + "left_line": 1305, + "right_line": 1305 + }, + { + "left_line": 1306, + "right_line": 1306 + }, + { + "left_line": 1307, + "right_line": 1307 + }, + { + "left_line": 1308, + "right_line": 1308 + }, + { + "left_line": 1309, + "right_line": 1309 + }, + { + "left_line": 1310, + "right_line": 1310 + }, + { + "left_line": 1311, + "right_line": 1311 + }, + { + "left_line": 1312, + "right_line": 1312 + }, + { + "left_line": 1313, + "right_line": 1313 + }, + { + "left_line": 1314, + "right_line": 1314 + }, + { + "left_line": 1315, + "right_line": 1315 + }, + { + "left_line": 1316, + "right_line": 1316 + }, + { + "left_line": 1317, + "right_line": 1317 + }, + { + "left_line": 1318, + "right_line": 1318 + }, + { + "left_line": 1319, + "right_line": 1319 + }, + { + "left_line": 1320, + "right_line": 1320 + }, + { + "left_line": 1321, + "right_line": 1321 + }, + { + "left_line": 1322, + "right_line": 1322 + }, + { + "left_line": 1323, + "right_line": 1323 + }, + { + "left_line": 1324, + "right_line": 1324 + }, + { + "left_line": 1325, + "right_line": 1325 + }, + { + "left_line": 1326, + "right_line": 1326 + }, + { + "left_line": 1327, + "right_line": 1327 + }, + { + "left_line": 1328, + "right_line": 1328 + }, + { + "left_line": 1329, + "right_line": 1329 + }, + { + "left_line": 1330, + "right_line": 1330 + }, + { + "left_line": 1331, + "right_line": 1331 + }, + { + "left_line": 1332, + "right_line": 1332 + }, + { + "left_line": 1333, + "right_line": 1333 + }, + { + "left_line": 1334, + "right_line": 1334 + }, + { + "left_line": 1335, + "right_line": 1335 + }, + { + "left_line": 1336, + "right_line": 1336 + }, + { + "left_line": 1337, + "right_line": 1337 + }, + { + "left_line": 1338, + "right_line": 1338 + }, + { + "left_line": 1339, + "right_line": 1339 + }, + { + "left_line": 1340, + "right_line": 1340 + }, + { + "left_line": 1341, + "right_line": 1341 + }, + { + "left_line": 1342, + "right_line": 1342 + }, + { + "left_line": 1343, + "right_line": 1343 + }, + { + "left_line": 1344, + "right_line": 1344 + }, + { + "left_line": 1345, + "right_line": 1345 + }, + { + "left_line": 1346, + "right_line": 1346 + }, + { + "left_line": 1347, + "right_line": 1347 + }, + { + "left_line": 1348, + "right_line": 1348 + }, + { + "left_line": 1349, + "right_line": 1349 + }, + { + "left_line": 1350, + "right_line": 1350 + }, + { + "left_line": 1351, + "right_line": 1351 + }, + { + "left_line": 1352, + "right_line": 1352 + }, + { + "left_line": 1353, + "right_line": 1353 + }, + { + "left_line": 1354, + "right_line": 1354 + }, + { + "left_line": 1355, + "right_line": 1355 + }, + { + "left_line": 1356, + "right_line": 1356 + }, + { + "left_line": 1357, + "right_line": 1357 + }, + { + "left_line": 1358, + "right_line": 1358 + }, + { + "left_line": 1359, + "right_line": 1359 + }, + { + "left_line": 1360, + "right_line": 1360 + }, + { + "left_line": 1361, + "right_line": 1361 + }, + { + "left_line": 1362, + "right_line": 1362 + }, + { + "left_line": 1363, + "right_line": 1363 + }, + { + "left_line": 1364, + "right_line": 1364 + }, + { + "left_line": 1365, + "right_line": 1365 + }, + { + "left_line": 1366, + "right_line": 1366 + }, + { + "left_line": 1367, + "right_line": 1367 + }, + { + "left_line": 1368, + "right_line": 1368 + }, + { + "left_line": 1369, + "right_line": 1369 + }, + { + "left_line": 1370, + "right_line": 1370 + }, + { + "left_line": 1371, + "right_line": 1371 + }, + { + "left_line": 1372, + "right_line": 1372 + }, + { + "left_line": 1373, + "right_line": 1373 + }, + { + "left_line": 1374, + "right_line": 1374 + }, + { + "left_line": 1375, + "right_line": 1375 + }, + { + "left_line": 1376, + "right_line": 1376 + }, + { + "left_line": 1377, + "right_line": 1377 + }, + { + "left_line": 1378, + "right_line": 1378 + }, + { + "left_line": 1379, + "right_line": 1379 + }, + { + "left_line": 1380, + "right_line": 1380 + }, + { + "left_line": 1381, + "right_line": 1381 + }, + { + "left_line": 1382, + "right_line": 1382 + }, + { + "left_line": 1383, + "right_line": 1383 + }, + { + "left_line": 1384, + "right_line": 1384 + }, + { + "left_line": 1385, + "right_line": 1385 + }, + { + "left_line": 1386, + "right_line": 1386 + }, + { + "left_line": 1387, + "right_line": 1387 + }, + { + "left_line": 1388, + "right_line": 1388 + }, + { + "left_line": 1389, + "right_line": 1389 + }, + { + "left_line": 1390, + "right_line": 1390 + }, + { + "left_line": 1391, + "right_line": 1391 + }, + { + "left_line": 1392, + "right_line": 1392 + }, + { + "left_line": 1393, + "right_line": 1393 + }, + { + "left_line": 1394, + "right_line": 1394 + }, + { + "left_line": 1395, + "right_line": 1395 + }, + { + "left_line": 1396, + "right_line": 1396 + }, + { + "left_line": 1397, + "right_line": 1397 + }, + { + "left_line": 1398, + "right_line": 1398 + }, + { + "left_line": 1399, + "right_line": 1399 + }, + { + "left_line": 1400, + "right_line": 1400 + }, + { + "left_line": 1401, + "right_line": 1401 + }, + { + "left_line": 1402, + "right_line": 1402 + }, + { + "left_line": 1403, + "right_line": 1403 + }, + { + "left_line": 1404, + "right_line": 1404 + }, + { + "left_line": 1405, + "right_line": 1405 + }, + { + "left_line": 1406, + "right_line": 1406 + }, + { + "left_line": 1407, + "right_line": 1407 + }, + { + "left_line": 1408, + "right_line": 1408 + }, + { + "left_line": 1409, + "right_line": 1409 + }, + { + "left_line": 1410, + "right_line": 1410 + }, + { + "left_line": 1411, + "right_line": 1411 + }, + { + "left_line": 1412, + "right_line": 1412 + }, + { + "left_line": 1413, + "right_line": 1413 + }, + { + "left_line": 1414, + "right_line": 1414 + }, + { + "left_line": 1415, + "right_line": 1415 + }, + { + "left_line": 1416, + "right_line": 1416 + }, + { + "left_line": 1417, + "right_line": 1417 + }, + { + "left_line": 1418, + "right_line": 1418 + }, + { + "left_line": 1419, + "right_line": 1419 + }, + { + "left_line": 1420, + "right_line": 1420 + }, + { + "left_line": 1421, + "right_line": 1421 + }, + { + "left_line": 1422, + "right_line": 1422 + }, + { + "left_line": 1423, + "right_line": 1423 + }, + { + "left_line": 1424, + "right_line": 1424 + }, + { + "left_line": 1425, + "right_line": 1425 + }, + { + "left_line": 1426, + "right_line": 1426 + }, + { + "left_line": 1427, + "right_line": 1427 + }, + { + "left_line": 1428, + "right_line": 1428 + }, + { + "left_line": 1429, + "right_line": 1429 + }, + { + "left_line": 1430, + "right_line": 1430 + }, + { + "left_line": 1431, + "right_line": 1431 + }, + { + "left_line": 1432, + "right_line": 1432 + }, + { + "left_line": 1433, + "right_line": 1433 + }, + { + "left_line": 1434, + "right_line": 1434 + }, + { + "left_line": 1435, + "right_line": 1435 + }, + { + "left_line": 1436, + "right_line": 1436 + }, + { + "left_line": 1437, + "right_line": 1437 + }, + { + "left_line": 1438, + "right_line": 1438 + }, + { + "left_line": 1439, + "right_line": 1439 + }, + { + "left_line": 1440, + "right_line": 1440 + }, + { + "left_line": 1441, + "right_line": 1441 + }, + { + "left_line": 1442, + "right_line": 1442 + }, + { + "left_line": 1443, + "right_line": 1443 + }, + { + "left_line": 1444, + "right_line": 1444 + }, + { + "left_line": 1445, + "right_line": 1445 + }, + { + "left_line": 1446, + "right_line": 1446 + }, + { + "left_line": 1447, + "right_line": 1447 + }, + { + "left_line": 1448, + "right_line": 1448 + }, + { + "left_line": 1449, + "right_line": 1449 + }, + { + "left_line": 1450, + "right_line": 1450 + }, + { + "left_line": 1451, + "right_line": 1451 + }, + { + "left_line": 1452, + "right_line": 1452 + }, + { + "left_line": 1453, + "right_line": 1453 + }, + { + "left_line": 1454, + "right_line": 1454 + }, + { + "left_line": 1455, + "right_line": 1455 + }, + { + "left_line": 1456, + "right_line": 1456 + }, + { + "left_line": 1457, + "right_line": 1457 + }, + { + "left_line": 1458, + "right_line": 1458 + }, + { + "left_line": 1459, + "right_line": 1459 + }, + { + "left_line": 1460, + "right_line": 1460 + }, + { + "left_line": 1461, + "right_line": 1461 + }, + { + "left_line": 1462, + "right_line": 1462 + }, + { + "left_line": 1463, + "right_line": 1463 + }, + { + "left_line": 1464, + "right_line": 1464 + }, + { + "left_line": 1465, + "right_line": 1465 + }, + { + "left_line": 1466, + "right_line": 1466 + }, + { + "left_line": 1467, + "right_line": 1467 + }, + { + "left_line": 1468, + "right_line": 1468 + }, + { + "left_line": 1469, + "right_line": 1469 + }, + { + "left_line": 1470, + "right_line": 1470 + }, + { + "left_line": 1471, + "right_line": 1471 + }, + { + "left_line": 1472, + "right_line": 1472 + }, + { + "left_line": 1473, + "right_line": 1473 + }, + { + "left_line": 1474, + "right_line": 1474 + }, + { + "left_line": 1475, + "right_line": 1475 + }, + { + "left_line": 1476, + "right_line": 1476 + }, + { + "left_line": 1477, + "right_line": 1477 + }, + { + "left_line": 1478, + "right_line": 1478 + }, + { + "left_line": 1479, + "right_line": 1479 + }, + { + "left_line": 1480, + "right_line": 1480 + }, + { + "left_line": 1481, + "right_line": 1481 + }, + { + "left_line": 1482, + "right_line": 1482 + }, + { + "left_line": 1483, + "right_line": 1483 + }, + { + "left_line": 1484, + "right_line": 1484 + }, + { + "left_line": 1485, + "right_line": 1485 + }, + { + "left_line": 1486, + "right_line": 1486 + }, + { + "left_line": 1487, + "right_line": 1487 + }, + { + "left_line": 1488, + "right_line": 1488 + }, + { + "left_line": 1489, + "right_line": 1489 + }, + { + "left_line": 1490, + "right_line": 1490 + }, + { + "left_line": 1491, + "right_line": 1491 + }, + { + "left_line": 1492, + "right_line": 1492 + }, + { + "left_line": 1493, + "right_line": 1493 + }, + { + "left_line": 1494, + "right_line": 1494 + }, + { + "left_line": 1495, + "right_line": 1495 + }, + { + "left_line": 1496, + "right_line": 1496 + }, + { + "left_line": 1497, + "right_line": 1497 + }, + { + "left_line": 1498, + "right_line": 1498 + }, + { + "left_line": 1499, + "right_line": 1499 + }, + { + "left_line": 1500, + "right_line": 1500 + }, + { + "left_line": 1501, + "right_line": 1501 + }, + { + "left_line": 1502, + "right_line": 1502 + }, + { + "left_line": 1503, + "right_line": 1503 + }, + { + "left_line": 1504, + "right_line": 1504 + }, + { + "left_line": 1505, + "right_line": 1505 + }, + { + "left_line": 1506, + "right_line": 1506 + }, + { + "left_line": 1507, + "right_line": 1507 + }, + { + "left_line": 1508, + "right_line": 1508 + }, + { + "left_line": 1509, + "right_line": 1509 + }, + { + "left_line": 1510, + "right_line": 1510 + }, + { + "left_line": 1511, + "right_line": 1511 + }, + { + "left_line": 1512, + "right_line": 1512 + }, + { + "left_line": 1513, + "right_line": 1513 + }, + { + "left_line": 1514, + "right_line": 1514 + }, + { + "left_line": 1515, + "right_line": 1515 + }, + { + "left_line": 1516, + "right_line": 1516 + }, + { + "left_line": 1517, + "right_line": 1517 + }, + { + "left_line": 1518, + "right_line": 1518 + }, + { + "left_line": 1519, + "right_line": 1519 + }, + { + "left_line": 1520, + "right_line": 1520 + }, + { + "left_line": 1521, + "right_line": 1521 + }, + { + "left_line": 1522, + "right_line": 1522 + }, + { + "left_line": 1523, + "right_line": 1523 + }, + { + "left_line": 1524, + "right_line": 1524 + }, + { + "left_line": 1525, + "right_line": 1525 + }, + { + "left_line": 1526, + "right_line": 1526 + }, + { + "left_line": 1527, + "right_line": 1527 + }, + { + "left_line": 1528, + "right_line": 1528 + }, + { + "left_line": 1529, + "right_line": 1529 + }, + { + "left_line": 1530, + "right_line": 1530 + }, + { + "left_line": 1531, + "right_line": 1531 + }, + { + "left_line": 1532, + "right_line": 1532 + }, + { + "left_line": 1533, + "right_line": 1533 + }, + { + "left_line": 1534, + "right_line": 1534 + }, + { + "left_line": 1535, + "right_line": 1535 + }, + { + "left_line": 1536, + "right_line": 1536 + }, + { + "left_line": 1537, + "right_line": 1537 + }, + { + "left_line": 1538, + "right_line": 1538 + }, + { + "left_line": 1539, + "right_line": 1539 + }, + { + "left_line": 1540, + "right_line": 1540 + }, + { + "left_line": 1541, + "right_line": 1541 + }, + { + "left_line": 1542, + "right_line": 1542 + }, + { + "left_line": 1543, + "right_line": 1543 + }, + { + "left_line": 1544, + "right_line": 1544 + }, + { + "left_line": 1545, + "right_line": 1545 + }, + { + "left_line": 1546, + "right_line": 1546 + }, + { + "left_line": 1547, + "right_line": 1547 + }, + { + "left_line": 1548, + "right_line": 1548 + }, + { + "left_line": 1549, + "right_line": 1549 + }, + { + "left_line": 1550, + "right_line": 1550 + }, + { + "left_line": 1551, + "right_line": 1551 + }, + { + "left_line": 1552, + "right_line": 1552 + }, + { + "left_line": 1553, + "right_line": 1553 + }, + { + "left_line": 1554, + "right_line": 1554 + }, + { + "left_line": 1555, + "right_line": 1555 + }, + { + "left_line": 1556, + "right_line": 1556 + }, + { + "left_line": 1557, + "right_line": 1557 + }, + { + "left_line": 1558, + "right_line": 1558 + }, + { + "left_line": 1559, + "right_line": 1559 + }, + { + "left_line": 1560, + "right_line": 1560 + }, + { + "left_line": 1561, + "right_line": 1561 + }, + { + "left_line": 1562, + "right_line": 1562 + }, + { + "left_line": 1563, + "right_line": 1563 + }, + { + "left_line": 1564, + "right_line": 1564 + }, + { + "left_line": 1565, + "right_line": 1565 + }, + { + "left_line": 1566, + "right_line": 1566 + }, + { + "left_line": 1567, + "right_line": 1567 + }, + { + "left_line": 1568, + "right_line": 1568 + }, + { + "left_line": 1569, + "right_line": 1569 + }, + { + "left_line": 1570, + "right_line": 1570 + }, + { + "left_line": 1571, + "right_line": 1571 + }, + { + "left_line": 1572, + "right_line": 1572 + }, + { + "left_line": 1573, + "right_line": 1573 + }, + { + "left_line": 1574, + "right_line": 1574 + }, + { + "left_line": 1575, + "right_line": 1575 + }, + { + "left_line": 1576, + "right_line": 1576 + }, + { + "left_line": 1577, + "right_line": 1577 + }, + { + "left_line": 1578, + "right_line": 1578 + }, + { + "left_line": 1579, + "right_line": 1579 + }, + { + "left_line": 1580, + "right_line": 1580 + }, + { + "left_line": 1581, + "right_line": 1581 + }, + { + "left_line": 1582, + "right_line": 1582 + }, + { + "left_line": 1583, + "right_line": 1583 + }, + { + "left_line": 1584, + "right_line": 1584 + }, + { + "left_line": 1585, + "right_line": 1585 + }, + { + "left_line": 1586, + "right_line": 1586 + }, + { + "left_line": 1587, + "right_line": 1587 + }, + { + "left_line": 1588, + "right_line": 1588 + }, + { + "left_line": 1589, + "right_line": 1589 + }, + { + "left_line": 1590, + "right_line": 1590 + }, + { + "left_line": 1591, + "right_line": 1591 + }, + { + "left_line": 1592, + "right_line": 1592 + }, + { + "left_line": 1593, + "right_line": 1593 + }, + { + "left_line": 1594, + "right_line": 1594 + }, + { + "left_line": 1595, + "right_line": 1595 + }, + { + "left_line": 1596, + "right_line": 1596 + }, + { + "left_line": 1597, + "right_line": 1597 + }, + { + "left_line": 1598, + "right_line": 1598 + }, + { + "left_line": 1599, + "right_line": 1599 + }, + { + "left_line": 1600, + "right_line": 1600 + }, + { + "left_line": 1601, + "right_line": 1601 + }, + { + "left_line": 1602, + "right_line": 1602 + }, + { + "left_line": 1603, + "right_line": 1603 + }, + { + "left_line": 1604, + "right_line": 1604 + }, + { + "left_line": 1605, + "right_line": 1605 + }, + { + "left_line": 1606, + "right_line": 1606 + }, + { + "left_line": 1607, + "right_line": 1607 + }, + { + "left_line": 1608, + "right_line": 1608 + }, + { + "left_line": 1609, + "right_line": 1609 + }, + { + "left_line": 1610, + "right_line": 1610 + }, + { + "left_line": 1611, + "right_line": 1611 + }, + { + "left_line": 1612, + "right_line": 1612 + }, + { + "left_line": 1613, + "right_line": 1613 + }, + { + "left_line": 1614, + "right_line": 1614 + }, + { + "left_line": 1615, + "right_line": 1615 + }, + { + "left_line": 1616, + "right_line": 1616 + }, + { + "left_line": 1617, + "right_line": 1617 + }, + { + "left_line": 1618, + "right_line": 1618 + }, + { + "left_line": 1619, + "right_line": 1619 + }, + { + "left_line": 1620, + "right_line": 1620 + }, + { + "left_line": 1621, + "right_line": 1621 + }, + { + "left_line": 1622, + "right_line": 1622 + }, + { + "left_line": 1623, + "right_line": 1623 + }, + { + "left_line": 1624, + "right_line": 1624 + }, + { + "left_line": 1625, + "right_line": 1625 + }, + { + "left_line": 1626, + "right_line": 1626 + }, + { + "left_line": 1627, + "right_line": 1627 + }, + { + "left_line": 1628, + "right_line": 1628 + }, + { + "left_line": 1629, + "right_line": 1629 + }, + { + "left_line": 1630, + "right_line": 1630 + }, + { + "left_line": 1631, + "right_line": 1631 + }, + { + "left_line": 1632, + "right_line": 1632 + }, + { + "left_line": 1633, + "right_line": 1633 + }, + { + "left_line": 1634, + "right_line": 1634 + }, + { + "left_line": 1635, + "right_line": 1635 + }, + { + "left_line": 1636, + "right_line": 1636 + }, + { + "left_line": 1637, + "right_line": 1637 + }, + { + "left_line": 1638, + "right_line": 1638 + }, + { + "left_line": 1639, + "right_line": 1639 + }, + { + "left_line": 1640, + "right_line": 1640 + }, + { + "left_line": 1641, + "right_line": 1641 + }, + { + "left_line": 1642, + "right_line": 1642 + }, + { + "left_line": 1643, + "right_line": 1643 + }, + { + "left_line": 1644, + "right_line": 1644 + }, + { + "left_line": 1645, + "right_line": 1645 + }, + { + "left_line": 1646, + "right_line": 1646 + }, + { + "left_line": 1647, + "right_line": 1647 + }, + { + "left_line": 1648, + "right_line": 1648 + }, + { + "left_line": 1649, + "right_line": 1649 + }, + { + "left_line": 1650, + "right_line": 1650 + }, + { + "left_line": 1651, + "right_line": 1651 + }, + { + "left_line": 1652, + "right_line": 1652 + }, + { + "left_line": 1653, + "right_line": 1653 + }, + { + "left_line": 1654, + "right_line": 1654 + }, + { + "left_line": 1655, + "right_line": 1655 + }, + { + "left_line": 1656, + "right_line": 1656 + }, + { + "left_line": 1657, + "right_line": 1657 + }, + { + "left_line": 1658, + "right_line": 1658 + }, + { + "left_line": 1659, + "right_line": 1659 + }, + { + "left_line": 1660, + "right_line": 1660 + }, + { + "left_line": 1661, + "right_line": 1661 + }, + { + "left_line": 1662, + "right_line": 1662 + }, + { + "left_line": 1663, + "right_line": 1663 + }, + { + "left_line": 1664, + "right_line": 1664 + }, + { + "left_line": 1665, + "right_line": 1665 + }, + { + "left_line": 1666, + "right_line": 1666 + }, + { + "left_line": 1667, + "right_line": 1667 + }, + { + "left_line": 1668, + "right_line": 1668 + }, + { + "left_line": 1669, + "right_line": 1669 + }, + { + "left_line": 1670, + "right_line": 1670 + }, + { + "left_line": 1671, + "right_line": 1671 + }, + { + "left_line": 1672, + "right_line": 1672 + }, + { + "left_line": 1673, + "right_line": 1673 + }, + { + "left_line": 1674, + "right_line": 1674 + }, + { + "left_line": 1675, + "right_line": 1675 + }, + { + "left_line": 1676, + "right_line": 1676 + }, + { + "left_line": 1677, + "right_line": 1677 + }, + { + "left_line": 1678, + "right_line": 1678 + }, + { + "left_line": 1679, + "right_line": 1679 + }, + { + "left_line": 1680, + "right_line": 1680 + }, + { + "left_line": 1681, + "right_line": 1681 + }, + { + "left_line": 1682, + "right_line": 1682 + }, + { + "left_line": 1683, + "right_line": 1683 + }, + { + "left_line": 1684, + "right_line": 1684 + }, + { + "left_line": 1685, + "right_line": 1685 + }, + { + "left_line": 1686, + "right_line": 1686 + }, + { + "left_line": 1687, + "right_line": 1687 + }, + { + "left_line": 1688, + "right_line": 1688 + }, + { + "left_line": 1689, + "right_line": 1689 + }, + { + "left_line": 1690, + "right_line": 1690 + }, + { + "left_line": 1691, + "right_line": 1691 + }, + { + "left_line": 1692, + "right_line": 1692 + }, + { + "left_line": 1693, + "right_line": 1693 + }, + { + "left_line": 1694, + "right_line": 1694 + }, + { + "left_line": 1695, + "right_line": 1695 + }, + { + "left_line": 1696, + "right_line": 1696 + }, + { + "left_line": 1697, + "right_line": 1697 + }, + { + "left_line": 1698, + "right_line": 1698 + }, + { + "left_line": 1699, + "right_line": 1699 + }, + { + "left_line": 1700, + "right_line": 1700 + }, + { + "left_line": 1701, + "right_line": 1701 + }, + { + "left_line": 1702, + "right_line": 1702 + }, + { + "left_line": 1703, + "right_line": 1703 + }, + { + "left_line": 1704, + "right_line": 1704 + }, + { + "left_line": 1705, + "right_line": 1705 + }, + { + "left_line": 1706, + "right_line": 1706 + }, + { + "left_line": 1707, + "right_line": 1707 + }, + { + "left_line": 1708, + "right_line": 1708 + }, + { + "left_line": 1709, + "right_line": 1709 + }, + { + "left_line": 1710, + "right_line": 1710 + }, + { + "left_line": 1711, + "right_line": 1711 + }, + { + "left_line": 1712, + "right_line": 1712 + }, + { + "left_line": 1713, + "right_line": 1713 + }, + { + "left_line": 1714, + "right_line": 1714 + }, + { + "left_line": 1715, + "right_line": 1715 + }, + { + "left_line": 1716, + "right_line": 1716 + }, + { + "left_line": 1717, + "right_line": 1717 + }, + { + "left_line": 1718, + "right_line": 1718 + }, + { + "left_line": 1719, + "right_line": 1719 + }, + { + "left_line": 1720, + "right_line": 1720 + }, + { + "left_line": 1721, + "right_line": 1721 + }, + { + "left_line": 1722, + "right_line": 1722 + }, + { + "left_line": 1723, + "right_line": 1723 + }, + { + "left_line": 1724, + "right_line": 1724 + }, + { + "left_line": 1725, + "right_line": 1725 + }, + { + "left_line": 1726, + "right_line": 1726 + }, + { + "left_line": 1727, + "right_line": 1727 + }, + { + "left_line": 1728, + "right_line": 1728 + }, + { + "left_line": 1729, + "right_line": 1729 + }, + { + "left_line": 1730, + "right_line": 1730 + }, + { + "left_line": 1731, + "right_line": 1731 + }, + { + "left_line": 1732, + "right_line": 1732 + }, + { + "left_line": 1733, + "right_line": 1733 + }, + { + "left_line": 1734, + "right_line": 1734 + }, + { + "left_line": 1735, + "right_line": 1735 + }, + { + "left_line": 1736, + "right_line": 1736 + }, + { + "left_line": 1737, + "right_line": 1737 + }, + { + "left_line": 1738, + "right_line": 1738 + }, + { + "left_line": 1739, + "right_line": 1739 + }, + { + "left_line": 1740, + "right_line": 1740 + }, + { + "left_line": 1741, + "right_line": 1741 + }, + { + "left_line": 1742, + "right_line": 1742 + }, + { + "left_line": 1743, + "right_line": 1743 + }, + { + "left_line": 1744, + "right_line": 1744 + }, + { + "left_line": 1745, + "right_line": 1745 + }, + { + "left_line": 1746, + "right_line": 1746 + }, + { + "left_line": 1747, + "right_line": 1747 + }, + { + "left_line": 1748, + "right_line": 1748 + }, + { + "left_line": 1749, + "right_line": 1749 + }, + { + "left_line": 1750, + "right_line": 1750 + }, + { + "left_line": 1751, + "right_line": 1751 + }, + { + "left_line": 1752, + "right_line": 1752 + }, + { + "left_line": 1753, + "right_line": 1753 + }, + { + "left_line": 1754, + "right_line": 1754 + }, + { + "left_line": 1755, + "right_line": 1755 + }, + { + "left_line": 1756, + "right_line": 1756 + }, + { + "left_line": 1757, + "right_line": 1757 + }, + { + "left_line": 1758, + "right_line": 1758 + }, + { + "left_line": 1759, + "right_line": 1759 + }, + { + "left_line": 1760, + "right_line": 1760 + }, + { + "left_line": 1761, + "right_line": 1761 + }, + { + "left_line": 1762, + "right_line": 1762 + }, + { + "left_line": 1763, + "right_line": 1763 + }, + { + "left_line": 1764, + "right_line": 1764 + }, + { + "left_line": 1765, + "right_line": 1765 + }, + { + "left_line": 1766, + "right_line": 1766 + }, + { + "left_line": 1767, + "right_line": 1767 + }, + { + "left_line": 1768, + "right_line": 1768 + }, + { + "left_line": 1769, + "right_line": 1769 + }, + { + "left_line": 1770, + "right_line": 1770 + }, + { + "left_line": 1771, + "right_line": 1771 + }, + { + "left_line": 1772, + "right_line": 1772 + }, + { + "left_line": 1773, + "right_line": 1773 + }, + { + "left_line": 1774, + "right_line": 1774 + }, + { + "left_line": 1775, + "right_line": 1775 + }, + { + "left_line": 1776, + "right_line": 1776 + }, + { + "left_line": 1777, + "right_line": 1777 + }, + { + "left_line": 1778, + "right_line": 1778 + }, + { + "left_line": 1779, + "right_line": 1779 + }, + { + "left_line": 1780, + "right_line": 1780 + }, + { + "left_line": 1781, + "right_line": 1781 + }, + { + "left_line": 1782, + "right_line": 1782 + }, + { + "left_line": 1783, + "right_line": 1783 + }, + { + "left_line": 1784, + "right_line": 1784 + }, + { + "left_line": 1785, + "right_line": 1785 + }, + { + "left_line": 1786, + "right_line": 1786 + }, + { + "left_line": 1787, + "right_line": 1787 + }, + { + "left_line": 1788, + "right_line": 1788 + }, + { + "left_line": 1789, + "right_line": 1789 + }, + { + "left_line": 1790, + "right_line": 1790 + }, + { + "left_line": 1791, + "right_line": 1791 + }, + { + "left_line": 1792, + "right_line": 1792 + }, + { + "left_line": 1793, + "right_line": 1793 + }, + { + "left_line": 1794, + "right_line": 1794 + }, + { + "left_line": 1795, + "right_line": 1795 + }, + { + "left_line": 1796, + "right_line": 1796 + }, + { + "left_line": 1797, + "right_line": 1797 + }, + { + "left_line": 1798, + "right_line": 1798 + }, + { + "left_line": 1799, + "right_line": 1799 + }, + { + "left_line": 1800, + "right_line": 1800 + }, + { + "left_line": 1801, + "right_line": 1801 + }, + { + "left_line": 1802, + "right_line": 1802 + }, + { + "left_line": 1803, + "right_line": 1803 + }, + { + "left_line": 1804, + "right_line": 1804 + }, + { + "left_line": 1805, + "right_line": 1805 + }, + { + "left_line": 1806, + "right_line": 1806 + }, + { + "left_line": 1807, + "right_line": 1807 + }, + { + "left_line": 1808, + "right_line": 1808 + }, + { + "left_line": 1809, + "right_line": 1809 + }, + { + "left_line": 1810, + "right_line": 1810 + }, + { + "left_line": 1811, + "right_line": 1811 + }, + { + "left_line": 1812, + "right_line": 1812 + }, + { + "left_line": 1813, + "right_line": 1813 + }, + { + "left_line": 1814, + "right_line": 1814 + }, + { + "left_line": 1815, + "right_line": 1815 + }, + { + "left_line": 1816, + "right_line": 1816 + }, + { + "left_line": 1817, + "right_line": 1817 + }, + { + "left_line": 1818, + "right_line": 1818 + }, + { + "left_line": 1819, + "right_line": 1819 + }, + { + "left_line": 1820, + "right_line": 1820 + }, + { + "left_line": 1821, + "right_line": 1821 + }, + { + "left_line": 1822, + "right_line": 1822 + }, + { + "left_line": 1823, + "right_line": 1823 + }, + { + "left_line": 1824, + "right_line": 1824 + }, + { + "left_line": 1825, + "right_line": 1825 + }, + { + "left_line": 1826, + "right_line": 1826 + }, + { + "left_line": 1827, + "right_line": 1827 + }, + { + "left_line": 1828, + "right_line": 1828 + }, + { + "left_line": 1829, + "right_line": 1829 + }, + { + "left_line": 1830, + "right_line": 1830 + }, + { + "left_line": 1831, + "right_line": 1831 + }, + { + "left_line": 1832, + "right_line": 1832 + }, + { + "left_line": 1833, + "right_line": 1833 + }, + { + "left_line": 1834, + "right_line": 1834 + }, + { + "left_line": 1835, + "right_line": 1835 + }, + { + "left_line": 1836, + "right_line": 1836 + }, + { + "left_line": 1837, + "right_line": 1837 + }, + { + "left_line": 1838, + "right_line": 1838 + }, + { + "left_line": 1839, + "right_line": 1839 + }, + { + "left_line": 1840, + "right_line": 1840 + }, + { + "left_line": 1841, + "right_line": 1841 + }, + { + "left_line": 1842, + "right_line": 1842 + }, + { + "left_line": 1843, + "right_line": 1843 + }, + { + "left_line": 1844, + "right_line": 1844 + }, + { + "left_line": 1845, + "right_line": 1845 + }, + { + "left_line": 1846, + "right_line": 1846 + }, + { + "left_line": 1847, + "right_line": 1847 + }, + { + "left_line": 1848, + "right_line": 1848 + }, + { + "left_line": 1849, + "right_line": 1849 + }, + { + "left_line": 1850, + "right_line": 1850 + }, + { + "left_line": 1851, + "right_line": 1851 + }, + { + "left_line": 1852, + "right_line": 1852 + }, + { + "left_line": 1853, + "right_line": 1853 + }, + { + "left_line": 1854, + "right_line": 1854 + }, + { + "left_line": 1855, + "right_line": 1855 + }, + { + "left_line": 1856, + "right_line": 1856 + }, + { + "left_line": 1857, + "right_line": 1857 + }, + { + "left_line": 1858, + "right_line": 1858 + }, + { + "left_line": 1859, + "right_line": 1859 + }, + { + "left_line": 1860, + "right_line": 1860 + }, + { + "left_line": 1861, + "right_line": 1861 + }, + { + "left_line": 1862, + "right_line": 1862 + }, + { + "left_line": 1863, + "right_line": 1863 + }, + { + "left_line": 1864, + "right_line": 1864 + }, + { + "left_line": 1865, + "right_line": 1865 + }, + { + "left_line": 1866, + "right_line": 1866 + }, + { + "left_line": 1867, + "right_line": 1867 + }, + { + "left_line": 1868, + "right_line": 1868 + }, + { + "left_line": 1869, + "right_line": 1869 + }, + { + "left_line": 1870, + "right_line": 1870 + }, + { + "left_line": 1871, + "right_line": 1871 + }, + { + "left_line": 1872, + "right_line": 1872 + }, + { + "left_line": 1873, + "right_line": 1873 + }, + { + "left_line": 1874, + "right_line": 1874 + }, + { + "left_line": 1875, + "right_line": 1875 + }, + { + "left_line": 1876, + "right_line": 1876 + }, + { + "left_line": 1877, + "right_line": 1877 + }, + { + "left_line": 1878, + "right_line": 1878 + }, + { + "left_line": 1879, + "right_line": 1879 + }, + { + "left_line": 1880, + "right_line": 1880 + }, + { + "left_line": 1881, + "right_line": 1881 + }, + { + "left_line": 1882, + "right_line": 1882 + }, + { + "left_line": 1883, + "right_line": 1883 + }, + { + "left_line": 1884, + "right_line": 1884 + }, + { + "left_line": 1885, + "right_line": 1885 + }, + { + "left_line": 1886, + "right_line": 1886 + }, + { + "left_line": 1887, + "right_line": 1887 + }, + { + "left_line": 1888, + "right_line": 1888 + }, + { + "left_line": 1889, + "right_line": 1889 + }, + { + "left_line": 1890, + "right_line": 1890 + }, + { + "left_line": 1891, + "right_line": 1891 + }, + { + "left_line": 1892, + "right_line": 1892 + }, + { + "left_line": 1893, + "right_line": 1893 + }, + { + "left_line": 1894, + "right_line": 1894 + }, + { + "left_line": 1895, + "right_line": 1895 + }, + { + "left_line": 1896, + "right_line": 1896 + }, + { + "left_line": 1897, + "right_line": 1897 + }, + { + "left_line": 1898, + "right_line": 1898 + }, + { + "left_line": 1899, + "right_line": 1899 + }, + { + "left_line": 1900, + "right_line": 1900 + }, + { + "left_line": 1901, + "right_line": 1901 + }, + { + "left_line": 1902, + "right_line": 1902 + }, + { + "left_line": 1903, + "right_line": 1903 + }, + { + "left_line": 1904, + "right_line": 1904 + }, + { + "left_line": 1905, + "right_line": 1905 + }, + { + "left_line": 1906, + "right_line": 1906 + }, + { + "left_line": 1907, + "right_line": 1907 + }, + { + "left_line": 1908, + "right_line": 1908 + }, + { + "left_line": 1909, + "right_line": 1909 + }, + { + "left_line": 1910, + "right_line": 1910 + }, + { + "left_line": 1911, + "right_line": 1911 + }, + { + "left_line": 1912, + "right_line": 1912 + }, + { + "left_line": 1913, + "right_line": 1913 + }, + { + "left_line": 1914, + "right_line": 1914 + }, + { + "left_line": 1915, + "right_line": 1915 + }, + { + "left_line": 1916, + "right_line": 1916 + }, + { + "left_line": 1917, + "right_line": 1917 + }, + { + "left_line": 1918, + "right_line": 1918 + }, + { + "left_line": 1919, + "right_line": 1919 + }, + { + "left_line": 1920, + "right_line": 1920 + }, + { + "left_line": 1921, + "right_line": 1921 + }, + { + "left_line": 1922, + "right_line": 1922 + }, + { + "left_line": 1923, + "right_line": 1923 + }, + { + "left_line": 1924, + "right_line": 1924 + }, + { + "left_line": 1925, + "right_line": 1925 + }, + { + "left_line": 1926, + "right_line": 1926 + }, + { + "left_line": 1927, + "right_line": 1927 + }, + { + "left_line": 1928, + "right_line": 1928 + }, + { + "left_line": 1929, + "right_line": 1929 + }, + { + "left_line": 1930, + "right_line": 1930 + }, + { + "left_line": 1931, + "right_line": 1931 + }, + { + "left_line": 1932, + "right_line": 1932 + }, + { + "left_line": 1933, + "right_line": 1933 + }, + { + "left_line": 1934, + "right_line": 1934 + }, + { + "left_line": 1935, + "right_line": 1935 + }, + { + "left_line": 1936, + "right_line": 1936 + }, + { + "left_line": 1937, + "right_line": 1937 + }, + { + "left_line": 1938, + "right_line": 1938 + }, + { + "left_line": 1939, + "right_line": 1939 + }, + { + "left_line": 1940, + "right_line": 1940 + }, + { + "left_line": 1941, + "right_line": 1941 + }, + { + "left_line": 1942, + "right_line": 1942 + }, + { + "left_line": 1943, + "right_line": 1943 + }, + { + "left_line": 1944, + "right_line": 1944 + }, + { + "left_line": 1945, + "right_line": 1945 + }, + { + "left_line": 1946, + "right_line": 1946 + }, + { + "left_line": 1947, + "right_line": 1947 + }, + { + "left_line": 1948, + "right_line": 1948 + }, + { + "left_line": 1949, + "right_line": 1949 + }, + { + "left_line": 1950, + "right_line": 1950 + }, + { + "left_line": 1951, + "right_line": 1951 + }, + { + "left_line": 1952, + "right_line": 1952 + }, + { + "left_line": 1953, + "right_line": 1953 + }, + { + "left_line": 1954, + "right_line": 1954 + }, + { + "left_line": 1955, + "right_line": 1955 + }, + { + "left_line": 1956, + "right_line": 1956 + }, + { + "left_line": 1957, + "right_line": 1957 + }, + { + "left_line": 1958, + "right_line": 1958 + }, + { + "left_line": 1959, + "right_line": 1959 + }, + { + "left_line": 1960, + "right_line": 1960 + }, + { + "left_line": 1961, + "right_line": 1961 + }, + { + "left_line": 1962, + "right_line": 1962 + }, + { + "left_line": 1963, + "right_line": 1963 + }, + { + "left_line": 1964, + "right_line": 1964 + }, + { + "left_line": 1965, + "right_line": 1965 + }, + { + "left_line": 1966, + "right_line": 1966 + }, + { + "left_line": 1967, + "right_line": 1967 + }, + { + "left_line": 1968, + "right_line": 1968 + }, + { + "left_line": 1969, + "right_line": 1969 + }, + { + "left_line": 1970, + "right_line": 1970 + }, + { + "left_line": 1971, + "right_line": 1971 + }, + { + "left_line": 1972, + "right_line": 1972 + }, + { + "left_line": 1973, + "right_line": 1973 + }, + { + "left_line": 1974, + "right_line": 1974 + }, + { + "left_line": 1975, + "right_line": 1975 + }, + { + "left_line": 1976, + "right_line": 1976 + }, + { + "left_line": 1977, + "right_line": 1977 + }, + { + "left_line": 1978, + "right_line": 1978 + }, + { + "left_line": 1979, + "right_line": 1979 + }, + { + "left_line": 1980, + "right_line": 1980 + }, + { + "left_line": 1981, + "right_line": 1981 + }, + { + "left_line": 1982, + "right_line": 1982 + }, + { + "left_line": 1983, + "right_line": 1983 + }, + { + "left_line": 1984, + "right_line": 1984 + }, + { + "left_line": 1985, + "right_line": 1985 + }, + { + "left_line": 1986, + "right_line": 1986 + }, + { + "left_line": 1987, + "right_line": 1987 + }, + { + "left_line": 1988, + "right_line": 1988 + }, + { + "left_line": 1989, + "right_line": 1989 + }, + { + "left_line": 1990, + "right_line": 1990 + }, + { + "left_line": 1991, + "right_line": 1991 + }, + { + "left_line": 1992, + "right_line": 1992 + }, + { + "left_line": 1993, + "right_line": 1993 + }, + { + "left_line": 1994, + "right_line": 1994 + }, + { + "left_line": 1995, + "right_line": 1995 + }, + { + "left_line": 1996, + "right_line": 1996 + }, + { + "left_line": 1997, + "right_line": 1997 + }, + { + "left_line": 1998, + "right_line": 1998 + }, + { + "left_line": 1999, + "right_line": 1999 + }, + { + "left_line": 2000, + "right_line": 2000 + }, + { + "left_line": 2001, + "right_line": 2001 + }, + { + "left_line": 2002, + "right_line": 2002 + }, + { + "left_line": 2003, + "right_line": 2003 + }, + { + "left_line": 2004, + "right_line": 2004 + }, + { + "left_line": 2005, + "right_line": 2005 + }, + { + "left_line": 2006, + "right_line": 2006 + }, + { + "left_line": 2007, + "right_line": 2007 + }, + { + "left_line": 2008, + "right_line": 2008 + }, + { + "left_line": 2009, + "right_line": 2009 + }, + { + "left_line": 2010, + "right_line": 2010 + }, + { + "left_line": 2011, + "right_line": 2011 + }, + { + "left_line": 2012, + "right_line": 2012 + }, + { + "left_line": 2013, + "right_line": 2013 + }, + { + "left_line": 2014, + "right_line": 2014 + }, + { + "left_line": 2015, + "right_line": 2015 + }, + { + "left_line": 2016, + "right_line": 2016 + }, + { + "left_line": 2017, + "right_line": 2017 + }, + { + "left_line": 2018, + "right_line": 2018 + }, + { + "left_line": 2019, + "right_line": 2019 + }, + { + "left_line": 2020, + "right_line": 2020 + }, + { + "left_line": 2021, + "right_line": 2021 + }, + { + "left_line": 2022, + "right_line": 2022 + }, + { + "left_line": 2023, + "right_line": 2023 + }, + { + "left_line": 2024, + "right_line": 2024 + }, + { + "left_line": 2025, + "right_line": 2025 + }, + { + "left_line": 2026, + "right_line": 2026 + }, + { + "left_line": 2027, + "right_line": 2027 + }, + { + "left_line": 2028, + "right_line": 2028 + }, + { + "left_line": 2029, + "right_line": 2029 + }, + { + "left_line": 2030, + "right_line": 2030 + }, + { + "left_line": 2031, + "right_line": 2031 + }, + { + "left_line": 2032, + "right_line": 2032 + }, + { + "left_line": 2033, + "right_line": 2033 + }, + { + "left_line": 2034, + "right_line": 2034 + }, + { + "left_line": 2035, + "right_line": 2035 + }, + { + "left_line": 2036, + "right_line": 2036 + }, + { + "left_line": 2037, + "right_line": 2037 + }, + { + "left_line": 2038, + "right_line": 2038 + }, + { + "left_line": 2039, + "right_line": 2039 + }, + { + "left_line": 2040, + "right_line": 2040 + }, + { + "left_line": 2041, + "right_line": 2041 + }, + { + "left_line": 2042, + "right_line": 2042 + }, + { + "left_line": 2043, + "right_line": 2043 + }, + { + "left_line": 2044, + "right_line": 2044 + }, + { + "left_line": 2045, + "right_line": 2045 + }, + { + "left_line": 2046, + "right_line": 2046 + }, + { + "left_line": 2047, + "right_line": 2047 + }, + { + "left_line": 2048, + "right_line": 2048 + }, + { + "left_line": 2049, + "right_line": 2049 + }, + { + "left_line": 2050, + "right_line": 2050 + }, + { + "left_line": 2051, + "right_line": 2051 + }, + { + "left_line": 2052, + "right_line": 2052 + }, + { + "left_line": 2053, + "right_line": 2053 + }, + { + "left_line": 2054, + "right_line": 2054 + }, + { + "left_line": 2055, + "right_line": 2055 + }, + { + "left_line": 2056, + "right_line": 2056 + }, + { + "left_line": 2057, + "right_line": 2057 + }, + { + "left_line": 2058, + "right_line": 2058 + }, + { + "left_line": 2059, + "right_line": 2059 + }, + { + "left_line": 2060, + "right_line": 2060 + }, + { + "left_line": 2061, + "right_line": 2061 + }, + { + "left_line": 2062, + "right_line": 2062 + }, + { + "left_line": 2063, + "right_line": 2063 + }, + { + "left_line": 2064, + "right_line": 2064 + }, + { + "left_line": 2065, + "right_line": 2065 + }, + { + "left_line": 2066, + "right_line": 2066 + }, + { + "left_line": 2067, + "right_line": 2067 + }, + { + "left_line": 2068, + "right_line": 2068 + }, + { + "left_line": 2069, + "right_line": 2069 + }, + { + "left_line": 2070, + "right_line": 2070 + }, + { + "left_line": 2071, + "right_line": 2071 + }, + { + "left_line": 2072, + "right_line": 2072 + }, + { + "left_line": 2073, + "right_line": 2073 + }, + { + "left_line": 2074, + "right_line": 2074 + }, + { + "left_line": 2075, + "right_line": 2075 + }, + { + "left_line": 2076, + "right_line": 2076 + }, + { + "left_line": 2077, + "right_line": 2077 + }, + { + "left_line": 2078, + "right_line": 2078 + }, + { + "left_line": 2079, + "right_line": 2079 + }, + { + "left_line": 2080, + "right_line": 2080 + }, + { + "left_line": 2081, + "right_line": 2081 + }, + { + "left_line": 2082, + "right_line": 2082 + }, + { + "left_line": 2083, + "right_line": 2083 + }, + { + "left_line": 2084, + "right_line": 2084 + }, + { + "left_line": 2085, + "right_line": 2085 + }, + { + "left_line": 2086, + "right_line": 2086 + }, + { + "left_line": 2087, + "right_line": 2087 + }, + { + "left_line": 2088, + "right_line": 2088 + }, + { + "left_line": 2089, + "right_line": 2089 + }, + { + "left_line": 2090, + "right_line": 2090 + }, + { + "left_line": 2091, + "right_line": 2091 + }, + { + "left_line": 2092, + "right_line": 2092 + }, + { + "left_line": 2093, + "right_line": 2093 + }, + { + "left_line": 2094, + "right_line": 2094 + }, + { + "left_line": 2095, + "right_line": 2095 + }, + { + "left_line": 2096, + "right_line": 2096 + }, + { + "left_line": 2097, + "right_line": 2097 + }, + { + "left_line": 2098, + "right_line": 2098 + }, + { + "left_line": 2099, + "right_line": 2099 + }, + { + "left_line": 2100, + "right_line": 2100 + }, + { + "left_line": 2101, + "right_line": 2101 + }, + { + "left_line": 2102, + "right_line": 2102 + }, + { + "left_line": 2103, + "right_line": 2103 + }, + { + "left_line": 2104, + "right_line": 2104 + }, + { + "left_line": 2105, + "right_line": 2105 + }, + { + "left_line": 2106, + "right_line": 2106 + }, + { + "left_line": 2107, + "right_line": 2107 + }, + { + "left_line": 2108, + "right_line": 2108 + }, + { + "left_line": 2109, + "right_line": 2109 + }, + { + "left_line": 2110, + "right_line": 2110 + }, + { + "left_line": 2111, + "right_line": 2111 + }, + { + "left_line": 2112, + "right_line": 2112 + }, + { + "left_line": 2113, + "right_line": 2113 + }, + { + "left_line": 2114, + "right_line": 2114 + }, + { + "left_line": 2115, + "right_line": 2115 + }, + { + "left_line": 2116, + "right_line": 2116 + }, + { + "left_line": 2117, + "right_line": 2117 + }, + { + "left_line": 2118, + "right_line": 2118 + }, + { + "left_line": 2119, + "right_line": 2119 + }, + { + "left_line": 2120, + "right_line": 2120 + }, + { + "left_line": 2121, + "right_line": 2121 + }, + { + "left_line": 2122, + "right_line": 2122 + }, + { + "left_line": 2123, + "right_line": 2123 + }, + { + "left_line": 2124, + "right_line": 2124 + }, + { + "left_line": 2125, + "right_line": 2125 + }, + { + "left_line": 2126, + "right_line": 2126 + }, + { + "left_line": 2127, + "right_line": 2127 + }, + { + "left_line": 2128, + "right_line": 2128 + }, + { + "left_line": 2129, + "right_line": 2129 + }, + { + "left_line": 2130, + "right_line": 2130 + }, + { + "left_line": 2131, + "right_line": 2131 + }, + { + "left_line": 2132, + "right_line": 2132 + }, + { + "left_line": 2133, + "right_line": 2133 + }, + { + "left_line": 2134, + "right_line": 2134 + }, + { + "left_line": 2135, + "right_line": 2135 + }, + { + "left_line": 2136, + "right_line": 2136 + }, + { + "left_line": 2137, + "right_line": 2137 + }, + { + "left_line": 2138, + "right_line": 2138 + }, + { + "left_line": 2139, + "right_line": 2139 + }, + { + "left_line": 2140, + "right_line": 2140 + }, + { + "left_line": 2141, + "right_line": 2141 + }, + { + "left_line": 2142, + "right_line": 2142 + }, + { + "left_line": 2143, + "right_line": 2143 + }, + { + "left_line": 2144, + "right_line": 2144 + }, + { + "left_line": 2145, + "right_line": 2145 + }, + { + "left_line": 2146, + "right_line": 2146 + }, + { + "left_line": 2147, + "right_line": 2147 + }, + { + "left_line": 2148, + "right_line": 2148 + }, + { + "left_line": 2149, + "right_line": 2149 + }, + { + "left_line": 2150, + "right_line": 2150 + }, + { + "left_line": 2151, + "right_line": 2151 + }, + { + "left_line": 2152, + "right_line": 2152 + }, + { + "left_line": 2153, + "right_line": 2153 + }, + { + "left_line": 2154, + "right_line": 2154 + }, + { + "left_line": 2155, + "right_line": 2155 + }, + { + "left_line": 2156, + "right_line": 2156 + }, + { + "left_line": 2157, + "right_line": 2157 + }, + { + "left_line": 2158, + "right_line": 2158 + }, + { + "left_line": 2159, + "right_line": 2159 + }, + { + "left_line": 2160, + "right_line": 2160 + }, + { + "left_line": 2161, + "right_line": 2161 + }, + { + "left_line": 2162, + "right_line": 2162 + }, + { + "left_line": 2163, + "right_line": 2163 + }, + { + "left_line": 2164, + "right_line": 2164 + }, + { + "left_line": 2165, + "right_line": 2165 + }, + { + "left_line": 2166, + "right_line": 2166 + }, + { + "left_line": 2167, + "right_line": 2167 + }, + { + "left_line": 2168, + "right_line": 2168 + }, + { + "left_line": 2169, + "right_line": 2169 + }, + { + "left_line": 2170, + "right_line": 2170 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/metadata.json b/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/metadata.json new file mode 100644 index 0000000..326680b --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "fa99a21461d4f1f5337b9b9d7a38a1b51c8f4e55", + "commit": "3fe8c38dc405586f7ad8f2ac748aa53e9c3615bd", + "message": "Fix regex in `etag_matches?` to prevent ReDoS (#2121)", + "author": "gecunps", + "date": "2025-10-08T22:50:03+08:00", + "language": "ruby", + "files_changed": 1, + "insertions": 1, + "deletions": 1 +} diff --git a/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/new.rb b/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/new.rb new file mode 100644 index 0000000..667e2e2 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/new.rb @@ -0,0 +1,2170 @@ +# frozen_string_literal: true + +# external dependencies +require 'rack' +begin + require 'rackup' +rescue LoadError +end +require 'tilt' +require 'rack/protection' +require 'rack/session' +require 'mustermann' +require 'mustermann/sinatra' +require 'mustermann/regular' + +# stdlib dependencies +require 'ipaddr' +require 'time' +require 'uri' + +# other files we need +require 'sinatra/indifferent_hash' +require 'sinatra/show_exceptions' +require 'sinatra/version' + +require_relative 'middleware/logger' + +module Sinatra + # The request object. See Rack::Request for more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Request + class Request < Rack::Request + HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze + HEADER_VALUE_WITH_PARAMS = %r{(?:(?:\w+|\*)/(?:\w+(?:\.|-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*}.freeze + + # Returns an array of acceptable media types for the response + def accept + @env['sinatra.accept'] ||= if @env.include?('HTTP_ACCEPT') && (@env['HTTP_ACCEPT'].to_s != '') + @env['HTTP_ACCEPT'] + .to_s + .scan(HEADER_VALUE_WITH_PARAMS) + .map! { |e| AcceptEntry.new(e) } + .sort + else + [AcceptEntry.new('*/*')] + end + end + + def accept?(type) + preferred_type(type).to_s.include?(type) + end + + def preferred_type(*types) + return accept.first if types.empty? + + types.flatten! + return types.first if accept.empty? + + accept.detect do |accept_header| + type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) } + return type if type + end + end + + alias secure? ssl? + + def forwarded? + !forwarded_authority.nil? + end + + def safe? + get? || head? || options? || trace? + end + + def idempotent? + safe? || put? || delete? || link? || unlink? + end + + def link? + request_method == 'LINK' + end + + def unlink? + request_method == 'UNLINK' + end + + def params + super + rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e + raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}" + rescue EOFError => e + raise BadRequest, "Invalid multipart/form-data: #{Rack::Utils.escape_html(e.message)}" + end + + class AcceptEntry + attr_accessor :params + attr_reader :entry + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @entry = entry + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + @q = @params.delete('q') { 1.0 }.to_f + end + + def <=>(other) + other.priority <=> priority + end + + def priority + # We sort in descending order; better matches should be higher. + [@q, -@type.count('*'), @params.size] + end + + def to_str + @type + end + + def to_s(full = false) + full ? entry : to_str + end + + def respond_to?(*args) + super || to_str.respond_to?(*args) + end + + def method_missing(*args, &block) + to_str.send(*args, &block) + end + end + + class MimeTypeEntry + attr_reader :params + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + end + + def accepts?(entry) + File.fnmatch(entry, self) && matches_params?(entry.params) + end + + def to_str + @type + end + + def matches_params?(params) + return true if @params.empty? + + params.all? { |k, v| !@params.key?(k) || @params[k] == v } + end + end + end + + # The response object. See Rack::Response and Rack::Response::Helpers for + # more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Response + # https://rubydoc.info/github/rack/rack/main/Rack/Response/Helpers + class Response < Rack::Response + DROP_BODY_RESPONSES = [204, 304].freeze + + def body=(value) + value = value.body while Rack::Response === value + @body = String === value ? [value.to_str] : value + end + + def each + block_given? ? super : enum_for(:each) + end + + def finish + result = body + + if drop_content_info? + headers.delete 'content-length' + headers.delete 'content-type' + end + + if drop_body? + close + result = [] + end + + if calculate_content_length? + # if some other code has already set content-length, don't muck with it + # currently, this would be the static file-handler + headers['content-length'] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] + end + + private + + def calculate_content_length? + headers['content-type'] && !headers['content-length'] && (Array === body) + end + + def drop_content_info? + informational? || drop_body? + end + + def drop_body? + DROP_BODY_RESPONSES.include?(status) + end + end + + # Some Rack handlers implement an extended body object protocol, however, + # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question. + # This middleware will detect an extended body object and will make sure it reaches the + # handler directly. We do this here, so our middleware and middleware set up by the app will + # still be able to run. + class ExtendedRack < Struct.new(:app) + def call(env) + result = app.call(env) + callback = env['async.callback'] + return result unless callback && async?(*result) + + after_response { callback.call result } + setup_close(env, *result) + throw :async + end + + private + + def setup_close(env, _status, _headers, body) + return unless body.respond_to?(:close) && env.include?('async.close') + + env['async.close'].callback { body.close } + env['async.close'].errback { body.close } + end + + def after_response(&block) + raise NotImplementedError, 'only supports EventMachine at the moment' unless defined? EventMachine + + EventMachine.next_tick(&block) + end + + def async?(status, _headers, body) + return true if status == -1 + + body.respond_to?(:callback) && body.respond_to?(:errback) + end + end + + # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing, + # if another CommonLogger is already in the middleware chain. + class CommonLogger < Rack::CommonLogger + def call(env) + env['sinatra.commonlogger'] ? @app.call(env) : super + end + + superclass.class_eval do + alias_method :call_without_check, :call unless method_defined? :call_without_check + def call(env) + env['sinatra.commonlogger'] = true + call_without_check(env) + end + end + end + + class Error < StandardError # :nodoc: + end + + class BadRequest < Error # :nodoc: + def http_status; 400 end + end + + class NotFound < Error # :nodoc: + def http_status; 404 end + end + + # Methods available to routes, before/after filters, and views. + module Helpers + # Set or retrieve the response status code. + def status(value = nil) + response.status = Rack::Utils.status_code(value) if value + response.status + end + + # Set or retrieve the response body. When a block is given, + # evaluation is deferred until the body is read with #each. + def body(value = nil, &block) + if block_given? + def block.each; yield(call) end + response.body = block + elsif value + unless request.head? || value.is_a?(Rack::Files::BaseIterator) || value.is_a?(Stream) + headers.delete 'content-length' + end + response.body = value + else + response.body + end + end + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) + # SERVER_PROTOCOL is required in Rack 3, fall back to HTTP_VERSION + # for servers not updated for Rack 3 (like Puma 5) + http_version = env['SERVER_PROTOCOL'] || env['HTTP_VERSION'] + if (http_version == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') + status 303 + else + status 302 + end + + # According to RFC 2616 section 14.30, "the field value consists of a + # single absolute URI" + response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?) + halt(*args) + end + + # Generates the absolute URI for a given path in the app. + # Takes Rack routers and reverse proxies into account. + def uri(addr = nil, absolute = true, add_script_name = true) + return addr if addr.to_s =~ /\A[a-z][a-z0-9+.\-]*:/i + + uri = [host = String.new] + if absolute + host << "http#{'s' if request.secure?}://" + host << if request.forwarded? || (request.port != (request.secure? ? 443 : 80)) + request.host_with_port + else + request.host + end + end + uri << request.script_name.to_s if add_script_name + uri << (addr || request.path_info).to_s + File.join uri + end + + alias url uri + alias to uri + + # Halt processing and return the error status provided. + def error(code, body = nil) + if code.respond_to? :to_str + body = code.to_str + code = 500 + end + response.body = body unless body.nil? + halt code + end + + # Halt processing and return a 404 Not Found. + def not_found(body = nil) + error 404, body + end + + # Set multiple response headers with Hash. + def headers(hash = nil) + response.headers.merge! hash if hash + response.headers + end + + # Access the underlying Rack session. + def session + request.session + end + + # Access shared logger object. + def logger + request.logger + end + + # Look up a media type by file extension in Rack's mime registry. + def mime_type(type) + Base.mime_type(type) + end + + # Set the content-type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) + return response['content-type'] unless type + + default = params.delete :default + mime_type = mime_type(type) || default + raise format('Unknown media type: %p', type) if mime_type.nil? + + mime_type = mime_type.dup + unless params.include?(:charset) || settings.add_charset.all? { |p| !(p === mime_type) } + params[:charset] = params.delete('charset') || settings.default_encoding + end + params.delete :charset if mime_type.include? 'charset' + unless params.empty? + mime_type << ';' + mime_type << params.map do |key, val| + val = val.inspect if val.to_s =~ /[";,]/ + "#{key}=#{val}" + end.join(';') + end + response['content-type'] = mime_type + end + + # https://html.spec.whatwg.org/#multipart-form-data + MULTIPART_FORM_DATA_REPLACEMENT_TABLE = { + '"' => '%22', + "\r" => '%0D', + "\n" => '%0A' + }.freeze + + # Set the Content-Disposition to "attachment" with the specified filename, + # instructing the user agents to prompt to save. + def attachment(filename = nil, disposition = :attachment) + response['Content-Disposition'] = disposition.to_s.dup + return unless filename + + params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)) + response['Content-Disposition'] << params + ext = File.extname(filename) + content_type(ext) unless response['content-type'] || ext.empty? + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) + if opts[:type] || !response['content-type'] + content_type opts[:type] || File.extname(path), default: 'application/octet-stream' + end + + disposition = opts[:disposition] + filename = opts[:filename] + disposition = :attachment if disposition.nil? && filename + filename = path if filename.nil? + attachment(filename, disposition) if disposition + + last_modified opts[:last_modified] if opts[:last_modified] + + file = Rack::Files.new(File.dirname(settings.app_file)) + result = file.serving(request, path) + + result[1].each { |k, v| headers[k] ||= v } + headers['content-length'] = result[1]['content-length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT + not_found + end + + # Class of the response body in case you use #stream. + # + # Three things really matter: The front and back block (back being the + # block generating content, front the one sending it to the client) and + # the scheduler, integrating with whatever concurrency feature the Rack + # handler is using. + # + # Scheduler has to respond to defer and schedule. + class Stream + def self.schedule(*) yield end + def self.defer(*) yield end + + def initialize(scheduler = self.class, keep_open = false, &back) + @back = back.to_proc + @scheduler = scheduler + @keep_open = keep_open + @callbacks = [] + @closed = false + end + + def close + return if closed? + + @closed = true + @scheduler.schedule { @callbacks.each { |c| c.call } } + end + + def each(&front) + @front = front + @scheduler.defer do + begin + @back.call(self) + rescue Exception => e + @scheduler.schedule { raise e } + ensure + close unless @keep_open + end + end + end + + def <<(data) + @scheduler.schedule { @front.call(data.to_s) } + self + end + + def callback(&block) + return yield if closed? + + @callbacks << block + end + + alias errback callback + + def closed? + @closed + end + end + + # Allows to start sending data to the client even though later parts of + # the response body have not yet been generated. + # + # The close parameter specifies whether Stream#close should be called + # after the block has been executed. + def stream(keep_open = false) + scheduler = env['async.callback'] ? EventMachine : Stream + current = @params.dup + stream = if scheduler == Stream && keep_open + Stream.new(scheduler, false) do |out| + until out.closed? + with_params(current) { yield(out) } + end + end + else + Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } + end + body stream + end + + # Specify response freshness policy for HTTP caches (Cache-Control header). + # Any number of non-value directives (:public, :private, :no_cache, + # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with + # a Hash of value directives (:max_age, :s_maxage). + # + # cache_control :public, :must_revalidate, :max_age => 60 + # => Cache-Control: public, must-revalidate, max-age=60 + # + # See RFC 2616 / 14.9 for more on standard cache control directives: + # http://tools.ietf.org/html/rfc2616#section-14.9.1 + def cache_control(*values) + if values.last.is_a?(Hash) + hash = values.pop + hash.reject! { |_k, v| v == false } + hash.reject! { |k, v| values << k if v == true } + else + hash = {} + end + + values.map! { |value| value.to_s.tr('_', '-') } + hash.each do |key, value| + key = key.to_s.tr('_', '-') + value = value.to_i if %w[max-age s-maxage].include? key + values << "#{key}=#{value}" + end + + response['Cache-Control'] = values.join(', ') if values.any? + end + + # Set the Expires header and Cache-Control/max-age directive. Amount + # can be an integer number of seconds in the future or a Time object + # indicating when the response should be considered "stale". The remaining + # "values" arguments are passed to the #cache_control helper: + # + # expires 500, :public, :must_revalidate + # => Cache-Control: public, must-revalidate, max-age=500 + # => Expires: Mon, 08 Jun 2009 08:50:17 GMT + # + def expires(amount, *values) + values << {} unless values.last.is_a?(Hash) + + if amount.is_a? Integer + time = Time.now + amount.to_i + max_age = amount + else + time = time_for amount + max_age = time - Time.now + end + + values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 } + cache_control(*values) + + response['Expires'] = time.httpdate + end + + # Set the last modified time of the resource (HTTP 'Last-Modified' header) + # and halt if conditional GET matches. The +time+ argument is a Time, + # DateTime, or other object that responds to +to_time+. + # + # When the current request includes an 'If-Modified-Since' header that is + # equal or later than the time specified, execution is immediately halted + # with a '304 Not Modified' response. + def last_modified(time) + return unless time + + time = time_for time + response['Last-Modified'] = time.httpdate + return if env['HTTP_IF_NONE_MATCH'] + + if (status == 200) && env['HTTP_IF_MODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i + halt 304 if since >= time.to_i + end + + if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i + halt 412 if since < time.to_i + end + rescue ArgumentError + end + + ETAG_KINDS = %i[strong weak].freeze + # Set the response entity tag (HTTP 'ETag' header) and halt if conditional + # GET matches. The +value+ argument is an identifier that uniquely + # identifies the current version of the resource. The +kind+ argument + # indicates whether the etag should be used as a :strong (default) or :weak + # cache validator. + # + # When the current request includes an 'If-None-Match' header with a + # matching etag, execution is immediately halted. If the request method is + # GET or HEAD, a '304 Not Modified' response is sent. + def etag(value, options = {}) + # Before touching this code, please double check RFC 2616 14.24 and 14.26. + options = { kind: options } unless Hash === options + kind = options[:kind] || :strong + new_resource = options.fetch(:new_resource) { request.post? } + + unless ETAG_KINDS.include?(kind) + raise ArgumentError, ':strong or :weak expected' + end + + value = format('"%s"', value) + value = "W/#{value}" if kind == :weak + response['ETag'] = value + + return unless success? || status == 304 + + if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource) + halt(request.safe? ? 304 : 412) + end + + if env['HTTP_IF_MATCH'] + return if etag_matches?(env['HTTP_IF_MATCH'], new_resource) + + halt 412 + end + + nil + end + + # Sugar for redirect (example: redirect back) + def back + request.referer + end + + # whether or not the status is set to 1xx + def informational? + status.between? 100, 199 + end + + # whether or not the status is set to 2xx + def success? + status.between? 200, 299 + end + + # whether or not the status is set to 3xx + def redirect? + status.between? 300, 399 + end + + # whether or not the status is set to 4xx + def client_error? + status.between? 400, 499 + end + + # whether or not the status is set to 5xx + def server_error? + status.between? 500, 599 + end + + # whether or not the status is set to 404 + def not_found? + status == 404 + end + + # whether or not the status is set to 400 + def bad_request? + status == 400 + end + + # Generates a Time object from the given value. + # Used by #expires and #last_modified. + def time_for(value) + if value.is_a? Numeric + Time.at value + elsif value.respond_to? :to_s + Time.parse value.to_s + else + value.to_time + end + rescue ArgumentError => e + raise e + rescue Exception + raise ArgumentError, "unable to convert #{value.inspect} to a Time object" + end + + private + + # Helper method checking if a ETag value list includes the current ETag. + def etag_matches?(list, new_resource = request.post?) + return !new_resource if list == '*' + + list.to_s.split(',').map(&:strip).include?(response['ETag']) + end + + def with_params(temp_params) + original = @params + @params = temp_params + yield + ensure + @params = original if original + end + end + + # Template rendering methods. Each method takes the name of a template + # to render as a Symbol and returns a String with the rendered output, + # as well as an optional hash with additional options. + # + # `template` is either the name or path of the template as symbol + # (Use `:'subdir/myview'` for views in subdirectories), or a string + # that will be rendered. + # + # Possible options are: + # :content_type The content type to use, same arguments as content_type. + # :layout If set to something falsy, no layout is rendered, otherwise + # the specified layout is used (Ignored for `sass`) + # :layout_engine Engine to use for rendering the layout. + # :locals A hash with local variables that should be available + # in the template + # :scope If set, template is evaluate with the binding of the given + # object rather than the application instance. + # :views Views directory to use. + module Templates + module ContentTyped + attr_accessor :content_type + end + + def initialize + super + @default_layout = :layout + @preferred_extension = nil + end + + def erb(template, options = {}, locals = {}, &block) + render(:erb, template, options, locals, &block) + end + + def haml(template, options = {}, locals = {}, &block) + render(:haml, template, options, locals, &block) + end + + def sass(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :sass, template, options, locals + end + + def scss(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :scss, template, options, locals + end + + def builder(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:builder, template, options, locals, &block) + end + + def liquid(template, options = {}, locals = {}, &block) + render(:liquid, template, options, locals, &block) + end + + def markdown(template, options = {}, locals = {}) + options[:exclude_outvar] = true + render :markdown, template, options, locals + end + + def rdoc(template, options = {}, locals = {}) + render :rdoc, template, options, locals + end + + def asciidoc(template, options = {}, locals = {}) + render :asciidoc, template, options, locals + end + + def markaby(template = nil, options = {}, locals = {}, &block) + render_ruby(:mab, template, options, locals, &block) + end + + def nokogiri(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:nokogiri, template, options, locals, &block) + end + + def slim(template, options = {}, locals = {}, &block) + render(:slim, template, options, locals, &block) + end + + def yajl(template, options = {}, locals = {}) + options[:default_content_type] = :json + render :yajl, template, options, locals + end + + def rabl(template, options = {}, locals = {}) + Rabl.register! + render :rabl, template, options, locals + end + + # Calls the given block for every possible template file in views, + # named name.ext, where ext is registered on engine. + def find_template(views, name, engine) + yield ::File.join(views, "#{name}.#{@preferred_extension}") + + Tilt.default_mapping.extensions_for(engine).each do |ext| + yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension + end + end + + private + + # logic shared between builder and nokogiri + def render_ruby(engine, template, options = {}, locals = {}, &block) + if template.is_a?(Hash) + options = template + template = nil + end + template = proc { block } if template.nil? + render engine, template, options, locals + end + + def render(engine, data, options = {}, locals = {}, &block) + # merge app-level options + engine_options = settings.respond_to?(engine) ? settings.send(engine) : {} + options.merge!(engine_options) { |_key, v1, _v2| v1 } + + # extract generic options + locals = options.delete(:locals) || locals || {} + views = options.delete(:views) || settings.views || './views' + layout = options[:layout] + layout = false if layout.nil? && options.include?(:layout) + eat_errors = layout.nil? + layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false) + layout = @default_layout if layout.nil? || (layout == true) + layout_options = options.delete(:layout_options) || {} + content_type = options.delete(:default_content_type) + content_type = options.delete(:content_type) || content_type + layout_engine = options.delete(:layout_engine) || engine + scope = options.delete(:scope) || self + exclude_outvar = options.delete(:exclude_outvar) + options.delete(:layout) + + # set some defaults + options[:outvar] ||= '@_out_buf' unless exclude_outvar + options[:default_encoding] ||= settings.default_encoding + + # compile and render template + begin + layout_was = @default_layout + @default_layout = false + template = compile_template(engine, data, options, views) + output = template.render(scope, locals, &block) + ensure + @default_layout = layout_was + end + + # render layout + if layout + extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope } + options = options.merge(extra_options).merge!(layout_options) + + catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } } + end + + if content_type + # sass-embedded returns a frozen string + output = +output + output.extend(ContentTyped).content_type = content_type + end + output + end + + def compile_template(engine, data, options, views) + eat_errors = options.delete :eat_errors + template = Tilt[engine] + raise "Template engine not found: #{engine}" if template.nil? + + case data + when Symbol + template_cache.fetch engine, data, options, views do + body, path, line = settings.templates[data] + if body + body = body.call if body.respond_to?(:call) + template.new(path, line.to_i, options) { body } + else + found = false + @preferred_extension = engine.to_s + find_template(views, data, template) do |file| + path ||= file # keep the initial path rather than the last one + found = File.exist?(file) + if found + path = file + break + end + end + throw :layout_missing if eat_errors && !found + template.new(path, 1, options) + end + end + when Proc + compile_block_template(template, options, &data) + when String + template_cache.fetch engine, data, options, views do + compile_block_template(template, options) { data } + end + else + raise ArgumentError, "Sorry, don't know how to render #{data.inspect}." + end + end + + def compile_block_template(template, options, &body) + first_location = caller_locations.first + path = first_location.path + line = first_location.lineno + path = options[:path] || path + line = options[:line] || line + template.new(path, line.to_i, options, &body) + end + end + + # Extremely simple template cache implementation. + # * Not thread-safe. + # * Size is unbounded. + # * Keys are not copied defensively, and should not be modified after + # being passed to #fetch. More specifically, the values returned by + # key#hash and key#eql? should not change. + # + # Implementation copied from Tilt::Cache. + class TemplateCache + def initialize + @cache = {} + end + + # Caches a value for key, or returns the previously cached value. + # If a value has been previously cached for key then it is + # returned. Otherwise, block is yielded to and its return value + # which may be nil, is cached under key and returned. + def fetch(*key) + @cache.fetch(key) do + @cache[key] = yield + end + end + + # Clears the cache. + def clear + @cache = {} + end + end + + # Base class for all Sinatra applications and middleware. + class Base + include Rack::Utils + include Helpers + include Templates + + URI_INSTANCE = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new + + attr_accessor :app, :env, :request, :response, :params + attr_reader :template_cache + + def initialize(app = nil, **_kwargs) + super() + @app = app + @template_cache = TemplateCache.new + @pinned_response = nil # whether a before! filter pinned the content-type + yield self if block_given? + end + + # Rack call interface. + def call(env) + dup.call!(env) + end + + def call!(env) # :nodoc: + @env = env + @params = IndifferentHash.new + @request = Request.new(env) + @response = Response.new + @pinned_response = nil + template_cache.clear if settings.reload_templates + + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + + unless @response['content-type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif (default = settings.default_content_type) + content_type default + end + end + + @response.finish + end + + # Access settings defined with Base.set. + def self.settings + self + end + + # Access settings defined with Base.set. + def settings + self.class.settings + end + + # Exit the current block, halts any further processing + # of the request, and returns the specified response. + def halt(*response) + response = response.first if response.length == 1 + throw :halt, response + end + + # Pass control to the next matching route. + # If there are no more matching routes, Sinatra will + # return a 404 response. + def pass(&block) + throw :pass, block + end + + # Forward the request to the downstream app -- middleware only. + def forward + raise 'downstream app not set' unless @app.respond_to? :call + + status, headers, body = @app.call env + @response.status = status + @response.body = body + @response.headers.merge! headers + nil + end + + private + + # Run filters defined on the class and all superclasses. + # Accepts an optional block to call after each filter is applied. + def filter!(type, base = settings, &block) + filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters) + base.filters[type].each do |args| + result = process_route(*args) + block.call(result) if block_given? + end + end + + # Run routes defined on the class and all superclasses. + def route!(base = settings, pass_block = nil) + routes = base.routes[@request.request_method] + + routes&.each do |pattern, conditions, block| + response.delete_header('content-type') unless @pinned_response + + returned_pass_block = process_route(pattern, conditions) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" + route_eval { block[*args] } + end + + # don't wipe out pass_block in superclass + pass_block = returned_pass_block if returned_pass_block + end + + # Run routes defined in superclass. + if base.superclass.respond_to?(:routes) + return route!(base.superclass, pass_block) + end + + route_eval(&pass_block) if pass_block + route_missing + end + + # Run a route block and throw :halt with the result. + def route_eval + throw :halt, yield + end + + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, conditions, block = nil, values = []) + route = @request.path_info + route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/') + + params = pattern.params(route) + return unless params + + params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes + force_encoding(params) + @params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any? + + regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) }) + if regexp_exists + captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c } + values += captures + @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty? + else + values += params.values.flatten + end + + catch(:pass) do + conditions.each { |c| throw :pass if c.bind(self).call == false } + block ? block[self, values] : yield(self, values) + end + rescue StandardError + @env['sinatra.error.params'] = @params + raise + ensure + params ||= {} + params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params'] + end + + # No matching route was found or all routes passed. The default + # implementation is to forward the request downstream when running + # as middleware (@app is non-nil); when no downstream app is set, raise + # a NotFound exception. Subclasses can override this method to perform + # custom route miss logic. + def route_missing + raise NotFound unless @app + + forward + end + + # Attempt to serve static files from public directory. Throws :halt when + # a matching file is found, returns nil otherwise. + # If custom static headers are defined, use them. + def static!(options = {}) + return if (public_dir = settings.public_folder).nil? + + path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" + return unless valid_path?(path) + + path = File.expand_path(path) + return unless path.start_with?("#{File.expand_path(public_dir)}/") + + return unless File.file?(path) + + env['sinatra.static_file'] = path + cache_control(*settings.static_cache_control) if settings.static_cache_control? + + headers(settings.static_headers) if settings.static_headers? + + send_file path, options.merge(disposition: nil) + end + + # Run the block with 'throw :halt' support and apply result to the response. + def invoke(&block) + res = catch(:halt, &block) + + res = [res] if (Integer === res) || (String === res) + if (Array === res) && (Integer === res.first) + res = res.dup + status(res.shift) + body(res.pop) + headers(*res) + elsif res.respond_to? :each + body res + end + nil # avoid double setting the same response tuple twice + end + + # Dispatch a request with error handling. + def dispatch! + # Avoid passing frozen string in force_encoding + @params.merge!(@request.params).each do |key, val| + next unless val.respond_to?(:force_encoding) + + val = val.dup if val.frozen? + @params[key] = force_encoding(val) + end + + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do + @pinned_response = !response['content-type'].nil? + end + route! + end + rescue ::Exception => e + invoke { handle_exception!(e) } + ensure + begin + filter! :after unless env['sinatra.static_file'] + rescue ::Exception => e + invoke { handle_exception!(e) } unless @env['sinatra.error'] + end + end + + # Error handling during requests. + def handle_exception!(boom) + error_params = @env['sinatra.error.params'] + + @params = @params.merge(error_params) if error_params + + @env['sinatra.error'] = boom + + http_status = if boom.is_a? Sinatra::Error + if boom.respond_to? :http_status + boom.http_status + elsif settings.use_code? && boom.respond_to?(:code) + boom.code + end + end + + http_status = 500 unless http_status&.between?(400, 599) + status(http_status) + + if server_error? + dump_errors! boom if settings.dump_errors? + raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler) + elsif not_found? + headers['X-Cascade'] = 'pass' if settings.x_cascade? + end + + if (res = error_block!(boom.class, boom) || error_block!(status, boom)) + return res + end + + if not_found? || bad_request? + if boom.message && boom.message != boom.class.name + body Rack::Utils.escape_html(boom.message) + else + content_type 'text/html' + body "

#{not_found? ? 'Not Found' : 'Bad Request'}

" + end + end + + return unless server_error? + + raise boom if settings.raise_errors? || settings.show_exceptions? + + error_block! Exception, boom + end + + # Find an custom error block for the key(s) specified. + def error_block!(key, *block_params) + base = settings + while base.respond_to?(:errors) + args_array = base.errors[key] + + next base = base.superclass unless args_array + + args_array.reverse_each do |args| + first = args == args_array.first + args += [block_params] + resp = process_route(*args) + return resp unless resp.nil? && !first + end + end + return false unless key.respond_to?(:superclass) && (key.superclass < Exception) + + error_block!(key.superclass, *block_params) + end + + def dump_errors!(boom) + if boom.respond_to?(:detailed_message) + msg = boom.detailed_message(highlight: false) + if msg =~ /\A(.*?)(?: \(#{ Regexp.quote(boom.class.to_s) }\))?\n/ + msg = $1 + additional_msg = $'.lines(chomp: true) + else + additional_msg = [] + end + else + msg = boom.message + additional_msg = [] + end + msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{msg}:", *additional_msg, *boom.backtrace].join("\n\t") + @env['rack.errors'].puts(msg) + end + + class << self + CALLERS_TO_IGNORE = [ # :nodoc: + %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code + %r{lib/tilt.*\.rb$}, # all tilt code + /^\(.*\)$/, # generated code + /\/bundled_gems.rb$/, # ruby >= 3.3 with bundler >= 2.5 + %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks + /active_support/, # active_support require hacks + %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks + /= 1.9.2 + %r{zeitwerk/(core_ext/)?kernel\.rb} # Zeitwerk kernel#require decorator + ].freeze + + attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback + + def callers_to_ignore + CALLERS_TO_IGNORE + end + + # Removes all routes, filters, middleware and extension hooks from the + # current class (not routes/filters/... defined by its superclass). + def reset! + @conditions = [] + @routes = {} + @filters = { before: [], after: [] } + @errors = {} + @middleware = [] + @prototype = nil + @extensions = [] + + @templates = if superclass.respond_to?(:templates) + Hash.new { |_hash, key| superclass.templates[key] } + else + {} + end + end + + # Extension modules registered on this class and all superclasses. + def extensions + if superclass.respond_to?(:extensions) + (@extensions + superclass.extensions).uniq + else + @extensions + end + end + + # Middleware used in this class and all superclasses. + def middleware + if superclass.respond_to?(:middleware) + superclass.middleware + @middleware + else + @middleware + end + end + + # Sets an option to the given value. If the value is a proc, + # the proc will be called every time the option is accessed. + def set(option, value = (not_set = true), ignore_setter = false, &block) + raise ArgumentError if block && !not_set + + if block + value = block + not_set = false + end + + if not_set + raise ArgumentError unless option.respond_to?(:each) + + option.each { |k, v| set(k, v) } + return self + end + + if respond_to?("#{option}=") && !ignore_setter + return __send__("#{option}=", value) + end + + setter = proc { |val| set option, val, true } + getter = proc { value } + + case value + when Proc + getter = value + when Symbol, Integer, FalseClass, TrueClass, NilClass + getter = value.inspect + when Hash + setter = proc do |val| + val = value.merge val if Hash === val + set option, val, true + end + end + + define_singleton("#{option}=", setter) + define_singleton(option, getter) + define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?" + self + end + + # Same as calling `set :option, true` for each of the given options. + def enable(*opts) + opts.each { |key| set(key, true) } + end + + # Same as calling `set :option, false` for each of the given options. + def disable(*opts) + opts.each { |key| set(key, false) } + end + + # Define a custom error handler. Optionally takes either an Exception + # class, or an HTTP status code to specify which errors should be + # handled. + def error(*codes, &block) + args = compile! 'ERROR', /.*/, block + codes = codes.flat_map(&method(:Array)) + codes << Exception if codes.empty? + codes << Sinatra::NotFound if codes.include?(404) + codes.each { |c| (@errors[c] ||= []) << args } + end + + # Sugar for `error(404) { ... }` + def not_found(&block) + error(404, &block) + end + + # Define a named template. The block must return the template source. + def template(name, &block) + filename, line = caller_locations.first + templates[name] = [block, filename, line.to_i] + end + + # Define the layout template. The block must return the template source. + def layout(name = :layout, &block) + template name, &block + end + + # Load embedded templates from the file; uses the caller's __FILE__ + # when no file is specified. + def inline_templates=(file = nil) + file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true + + begin + io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file) + app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2) + rescue Errno::ENOENT + app, data = nil + end + + return unless data + + encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m + $2 + else + settings.default_encoding + end + + lines = app.count("\n") + 1 + template = nil + force_encoding data, encoding + data.each_line do |line| + lines += 1 + if line =~ /^@@\s*(.*\S)\s*$/ + template = force_encoding(String.new, encoding) + templates[$1.to_sym] = [template, file, lines] + elsif template + template << line + end + end + end + + # Lookup or register a mime type in Rack's mime registry. + def mime_type(type, value = nil) + return type if type.nil? + return type.to_s if type.to_s.include?('/') + + type = ".#{type}" unless type.to_s[0] == '.' + return Rack::Mime.mime_type(type, nil) unless value + + Rack::Mime::MIME_TYPES[type] = value + end + + # provides all mime types matching type, including deprecated types: + # mime_types :html # => ['text/html'] + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type + if type =~ %r{^application/(xml|javascript)$} + [type, "text/#{$1}"] + elsif type =~ %r{^text/(xml|javascript)$} + [type, "application/#{$1}"] + else + [type] + end + end + + # Define a before filter; runs before all requests within the same + # context as route handlers and may access/modify the request and + # response. + def before(path = /.*/, **options, &block) + add_filter(:before, path, **options, &block) + end + + # Define an after filter; runs after all requests within the same + # context as route handlers and may access/modify the request and + # response. + def after(path = /.*/, **options, &block) + add_filter(:after, path, **options, &block) + end + + # add a filter + def add_filter(type, path = /.*/, **options, &block) + filters[type] << compile!(type, path, block, **options) + end + + def on_start(&on_start_callback) + @on_start_callback = on_start_callback + end + + def on_stop(&on_stop_callback) + @on_stop_callback = on_stop_callback + end + + # Add a route condition. The route is considered non-matching when the + # block returns false. + def condition(name = "#{caller.first[/`.*'/]} condition", &block) + @conditions << generate_method(name, &block) + end + + def public=(value) + warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead' + set(:public_folder, value) + end + + def public_dir=(value) + self.public_folder = value + end + + def public_dir + public_folder + end + + # Defining a `GET` handler also automatically defines + # a `HEAD` handler. + def get(path, opts = {}, &block) + conditions = @conditions.dup + route('GET', path, opts, &block) + + @conditions = conditions + route('HEAD', path, opts, &block) + end + + def put(path, opts = {}, &block) route 'PUT', path, opts, &block end + + def post(path, opts = {}, &block) route 'POST', path, opts, &block end + + def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end + + def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end + + def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end + + def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end + + def link(path, opts = {}, &block) route 'LINK', path, opts, &block end + + def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end + + # Makes the methods defined in the block and in the Modules given + # in `extensions` available to the handlers and templates + def helpers(*extensions, &block) + class_eval(&block) if block_given? + include(*extensions) if extensions.any? + end + + # Register an extension. Alternatively take a block from which an + # extension will be created and registered on the fly. + def register(*extensions, &block) + extensions << Module.new(&block) if block_given? + @extensions += extensions + extensions.each do |extension| + extend extension + extension.registered(self) if extension.respond_to?(:registered) + end + end + + def development?; environment == :development end + def production?; environment == :production end + def test?; environment == :test end + + # Set configuration options for Sinatra and/or the app. + # Allows scoping of settings for certain environments. + def configure(*envs) + yield self if envs.empty? || envs.include?(environment.to_sym) + end + + # Use the specified Rack middleware + def use(middleware, *args, &block) + @prototype = nil + @middleware << [middleware, args, block] + end + ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true) + + # Stop the self-hosted server if running. + def quit! + return unless running? + + # Use Thin's hard #stop! if available, otherwise just #stop. + running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop + warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages? + set :running_server, nil + set :handler_name, nil + + on_stop_callback.call unless on_stop_callback.nil? + end + + alias stop! quit! + + # Run the Sinatra app as a self-hosted server using + # Puma, Falcon (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + unless defined?(Rackup::Handler) + rackup_warning = <<~MISSING_RACKUP + Sinatra could not start, the required gems weren't found! + + Add them to your bundle with: + + bundle add rackup puma + + or install them with: + + gem install rackup puma + + MISSING_RACKUP + warn rackup_warning + exit 1 + end + + return if running? + + set options + handler = Rackup::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(Port: port, Host: bind) + + begin + start_server(handler, server_settings, handler_name, &block) + rescue Errno::EADDRINUSE + warn "== Someone is already performing on port #{port}!" + raise + ensure + quit! + end + end + + alias start! run! + + # Check whether the self-hosted server is running or not. + def running? + running_server? + end + + # The prototype instance used to process requests. + def prototype + @prototype ||= new + end + + # Create a new instance without middleware in front of it. + alias new! new unless method_defined? :new! + + # Create a new instance of the class fronted by its middleware + # pipeline. The object is guaranteed to respond to #call but may not be + # an instance of the class new was called on. + def new(*args, &block) + instance = new!(*args, &block) + Wrapper.new(build(instance).to_app, instance) + end + ruby2_keywords :new if respond_to?(:ruby2_keywords, true) + + # Creates a Rack::Builder instance with all the middleware set up and + # the given +app+ as end point. + def build(app) + builder = Rack::Builder.new + setup_default_middleware builder + setup_middleware builder + builder.run app + builder + end + + def call(env) + synchronize { prototype.call(env) } + end + + # Like Kernel#caller but excluding certain magic entries and without + # line / method information; the resulting array contains filenames only. + def caller_files + cleaned_caller(1).flatten + end + + private + + # Starts the server by running the Rack Handler. + def start_server(handler, server_settings, handler_name) + # Ensure we initialize middleware before startup, to match standard Rack + # behavior, by ensuring an instance exists: + prototype + # Run the instance we created: + handler.run(self, **server_settings) do |server| + unless suppress_messages? + warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}" + end + + setup_traps + set :running_server, server + set :handler_name, handler_name + server.threaded = settings.threaded if server.respond_to? :threaded= + on_start_callback.call unless on_start_callback.nil? + yield server if block_given? + end + end + + def suppress_messages? + handler_name =~ /cgi/i || quiet + end + + def setup_traps + return unless traps? + + at_exit { quit! } + + %i[INT TERM].each do |signal| + old_handler = trap(signal) do + quit! + old_handler.call if old_handler.respond_to?(:call) + end + end + + set :traps, false + end + + # Dynamically defines a method on settings. + def define_singleton(name, content = Proc.new) + singleton_class.class_eval do + undef_method(name) if method_defined? name + String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content) + end + end + + # Condition for matching host name. Parameter might be String or Regexp. + def host_name(pattern) + condition { pattern === request.host } + end + + # Condition for matching user agent. Parameter should be Regexp. + # Will set params[:agent]. + def user_agent(pattern) + condition do + if request.user_agent.to_s =~ pattern + @params[:agent] = $~[1..-1] + true + else + false + end + end + end + alias agent user_agent + + # Condition for matching mimetypes. Accepts file extensions. + def provides(*types) + types.map! { |t| mime_types(t) } + types.flatten! + condition do + response_content_type = response['content-type'] + preferred_type = request.preferred_type(types) + + if response_content_type + types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/]) + elsif preferred_type + params = (preferred_type.respond_to?(:params) ? preferred_type.params : {}) + content_type(preferred_type, params) + true + else + false + end + end + end + + def route(verb, path, options = {}, &block) + signature = compile!(verb, path, block, **options) + (@routes[verb] ||= []) << signature + invoke_hook(:route_added, verb, path, block) + signature + end + + def invoke_hook(name, *args) + extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } + end + + def generate_method(method_name, &block) + define_method(method_name, &block) + method = instance_method method_name + remove_method method_name + method + end + + def compile!(verb, path, block, **options) + # Because of self.options.host + host_name(options.delete(:host)) if options.key?(:host) + # Pass Mustermann opts to compile() + route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze + + options.each_pair { |option, args| send(option, *args) } + + pattern = compile(path, route_mustermann_opts) + method_name = "#{verb} #{path}" + unbound_method = generate_method(method_name, &block) + conditions = @conditions + @conditions = [] + wrapper = block.arity.zero? ? + proc { |a, _p| unbound_method.bind(a).call } : + proc { |a, p| unbound_method.bind(a).call(*p) } + + [pattern, conditions, wrapper] + end + + def compile(path, route_mustermann_opts = {}) + Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts)) + end + + def setup_default_middleware(builder) + builder.use ExtendedRack + builder.use ShowExceptions if show_exceptions? + builder.use Rack::MethodOverride if method_override? + builder.use Rack::Head + setup_logging builder + setup_sessions builder + setup_protection builder + setup_host_authorization builder + end + + def setup_middleware(builder) + middleware.each { |c, a, b| builder.use(c, *a, &b) } + end + + def setup_logging(builder) + if logging? + setup_common_logger(builder) + setup_custom_logger(builder) + elsif logging == false + setup_null_logger(builder) + end + end + + def setup_null_logger(builder) + builder.use Sinatra::Middleware::Logger, ::Logger::FATAL + end + + def setup_common_logger(builder) + builder.use Sinatra::CommonLogger + end + + def setup_custom_logger(builder) + if logging.respond_to? :to_int + builder.use Sinatra::Middleware::Logger, logging + else + builder.use Sinatra::Middleware::Logger + end + end + + def setup_protection(builder) + return unless protection? + + options = Hash === protection ? protection.dup : {} + options = { + img_src: "'self' data:", + font_src: "'self'" + }.merge options + + protect_session = options.fetch(:session) { sessions? } + options[:without_session] = !protect_session + + options[:reaction] ||= :drop_session + + builder.use Rack::Protection, options + end + + def setup_host_authorization(builder) + builder.use Rack::Protection::HostAuthorization, host_authorization + end + + def setup_sessions(builder) + return unless sessions? + + options = {} + options[:secret] = session_secret if session_secret? + options.merge! sessions.to_hash if sessions.respond_to? :to_hash + builder.use session_store, options + end + + def inherited(subclass) + subclass.reset! + subclass.set :app_file, caller_files.first unless subclass.app_file? + super + end + + @@mutex = Mutex.new + def synchronize(&block) + if lock? + @@mutex.synchronize(&block) + else + yield + end + end + + # used for deprecation warnings + def warn_for_deprecation(message) + warn message + "\n\tfrom #{cleaned_caller.first.join(':')}" + end + + # Like Kernel#caller but excluding certain magic entries + def cleaned_caller(keep = 3) + caller(1) + .map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] } + .reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } } + end + end + + # Force data to specified encoding. It defaults to settings.default_encoding + # which is UTF-8 by default + def self.force_encoding(data, encoding = default_encoding) + return if data == settings || data.is_a?(Tempfile) + + if data.respond_to? :force_encoding + data.force_encoding(encoding).encode! + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v, encoding) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v, encoding) } + end + data + end + + def force_encoding(*args) + settings.force_encoding(*args) + end + + reset! + + set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym + set :raise_errors, proc { test? } + set :dump_errors, proc { !test? } + set :show_exceptions, proc { development? } + set :sessions, false + set :session_store, Rack::Session::Cookie + set :logging, false + set :protection, true + set :method_override, false + set :use_code, false + set :default_encoding, 'utf-8' + set :x_cascade, true + set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" } + settings.add_charset << %r{^text/} + set :mustermann_opts, {} + set :default_content_type, 'text/html' + + # explicitly generating a session secret eagerly to play nice with preforking + begin + require 'securerandom' + set :session_secret, SecureRandom.hex(64) + rescue LoadError, NotImplementedError, RuntimeError + # SecureRandom raises a NotImplementedError if no random device is available + # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230 + set :session_secret, format('%064x', Kernel.rand((2**256) - 1)) + end + + class << self + alias methodoverride? method_override? + alias methodoverride= method_override= + end + + set :run, false # start server via at-exit hook? + set :running_server, nil + set :handler_name, nil + set :traps, true + set :server, %w[webrick] + set :bind, proc { development? ? 'localhost' : '0.0.0.0' } + set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567) + set :quiet, false + set :host_authorization, ->() do + if development? + { + permitted_hosts: [ + "localhost", + ".localhost", + ".test", + IPAddr.new("0.0.0.0/0"), + IPAddr.new("::/0"), + ] + } + else + {} + end + end + + ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'falcon' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + server.unshift 'puma' + + set :absolute_redirects, true + set :prefixed_redirects, false + set :strict_paths, true + + set :app_file, nil + set :root, proc { app_file && File.expand_path(File.dirname(app_file)) } + set :views, proc { root && File.join(root, 'views') } + set :reload_templates, proc { development? } + set :lock, false + set :threaded, true + + set :public_folder, proc { root && File.join(root, 'public') } + set :static, proc { public_folder && File.exist?(public_folder) } + set :static_cache_control, false + + set :static_headers, {} + + error ::Exception do + response.status = 500 + content_type 'text/html' + '

Internal Server Error

' + end + + configure :development do + get '/__sinatra__/:image.png' do + filename = __dir__ + "/images/#{params[:image].to_i}.png" + content_type :png + send_file filename + end + + error NotFound do + content_type 'text/html' + + if instance_of?(Sinatra::Application) + code = <<-RUBY.gsub(/^ {12}/, '') + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + RUBY + else + code = <<-RUBY.gsub(/^ {12}/, '') + class #{self.class} + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + end + RUBY + + file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '') + code = "# in #{file}\n#{code}" unless file.empty? + end + + <<-HTML.gsub(/^ {10}/, '') + + + + + + +

Sinatra doesn’t know this ditty.

+ +
+ Try this: +
#{Rack::Utils.escape_html(code)}
+
+ + + HTML + end + end + end + + # Execution context for classic style (top-level) applications. All + # DSL methods executed on main are delegated to this class. + # + # The Application class should not be subclassed, unless you want to + # inherit all settings, routes, handlers, and error pages from the + # top-level. Subclassing Sinatra::Base is highly recommended for + # modular applications. + class Application < Base + set :logging, proc { !test? } + set :method_override, true + set :run, proc { !test? } + set :app_file, nil + + def self.register(*extensions, &block) # :nodoc: + added_methods = extensions.flat_map(&:public_instance_methods) + Delegator.delegate(*added_methods) + super(*extensions, &block) + end + end + + # Sinatra delegation mixin. Mixing this module into an object causes all + # methods to be delegated to the Sinatra::Application class. Used primarily + # at the top-level. + module Delegator # :nodoc: + def self.delegate(*methods) + methods.each do |method_name| + define_method(method_name) do |*args, &block| + return super(*args, &block) if respond_to? method_name + + Delegator.target.send(method_name, *args, &block) + end + # ensure keyword argument passing is compatible with ruby >= 2.7 + ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true) + private method_name + end + end + + delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink, + :template, :layout, :before, :after, :error, :not_found, :configure, + :set, :mime_type, :enable, :disable, :use, :development?, :test?, + :production?, :helpers, :settings, :register, :on_start, :on_stop + + class << self + attr_accessor :target + end + + self.target = Application + end + + class Wrapper + def initialize(stack, instance) + @stack = stack + @instance = instance + end + + def settings + @instance.settings + end + + def helpers + @instance + end + + def call(env) + @stack.call(env) + end + + def inspect + "#<#{@instance.class} app_file=#{settings.app_file.inspect}>" + end + end + + # Create a new Sinatra application; the block is evaluated in the class scope. + def self.new(base = Base, &block) + base = Class.new(base) + base.class_eval(&block) if block_given? + base + end + + # Extend the top-level DSL with the modules provided. + def self.register(*extensions, &block) + Delegator.target.register(*extensions, &block) + end + + # Include the helper modules provided in Sinatra's request context. + def self.helpers(*extensions, &block) + Delegator.target.helpers(*extensions, &block) + end + + # Use the middleware for classic applications. + def self.use(*args, &block) + Delegator.target.use(*args, &block) + end +end diff --git a/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/old.rb b/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/old.rb new file mode 100644 index 0000000..81dfe3f --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_regex_in__etag_matches___t_7/old.rb @@ -0,0 +1,2170 @@ +# frozen_string_literal: true + +# external dependencies +require 'rack' +begin + require 'rackup' +rescue LoadError +end +require 'tilt' +require 'rack/protection' +require 'rack/session' +require 'mustermann' +require 'mustermann/sinatra' +require 'mustermann/regular' + +# stdlib dependencies +require 'ipaddr' +require 'time' +require 'uri' + +# other files we need +require 'sinatra/indifferent_hash' +require 'sinatra/show_exceptions' +require 'sinatra/version' + +require_relative 'middleware/logger' + +module Sinatra + # The request object. See Rack::Request for more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Request + class Request < Rack::Request + HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze + HEADER_VALUE_WITH_PARAMS = %r{(?:(?:\w+|\*)/(?:\w+(?:\.|-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*}.freeze + + # Returns an array of acceptable media types for the response + def accept + @env['sinatra.accept'] ||= if @env.include?('HTTP_ACCEPT') && (@env['HTTP_ACCEPT'].to_s != '') + @env['HTTP_ACCEPT'] + .to_s + .scan(HEADER_VALUE_WITH_PARAMS) + .map! { |e| AcceptEntry.new(e) } + .sort + else + [AcceptEntry.new('*/*')] + end + end + + def accept?(type) + preferred_type(type).to_s.include?(type) + end + + def preferred_type(*types) + return accept.first if types.empty? + + types.flatten! + return types.first if accept.empty? + + accept.detect do |accept_header| + type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) } + return type if type + end + end + + alias secure? ssl? + + def forwarded? + !forwarded_authority.nil? + end + + def safe? + get? || head? || options? || trace? + end + + def idempotent? + safe? || put? || delete? || link? || unlink? + end + + def link? + request_method == 'LINK' + end + + def unlink? + request_method == 'UNLINK' + end + + def params + super + rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e + raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}" + rescue EOFError => e + raise BadRequest, "Invalid multipart/form-data: #{Rack::Utils.escape_html(e.message)}" + end + + class AcceptEntry + attr_accessor :params + attr_reader :entry + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @entry = entry + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + @q = @params.delete('q') { 1.0 }.to_f + end + + def <=>(other) + other.priority <=> priority + end + + def priority + # We sort in descending order; better matches should be higher. + [@q, -@type.count('*'), @params.size] + end + + def to_str + @type + end + + def to_s(full = false) + full ? entry : to_str + end + + def respond_to?(*args) + super || to_str.respond_to?(*args) + end + + def method_missing(*args, &block) + to_str.send(*args, &block) + end + end + + class MimeTypeEntry + attr_reader :params + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + end + + def accepts?(entry) + File.fnmatch(entry, self) && matches_params?(entry.params) + end + + def to_str + @type + end + + def matches_params?(params) + return true if @params.empty? + + params.all? { |k, v| !@params.key?(k) || @params[k] == v } + end + end + end + + # The response object. See Rack::Response and Rack::Response::Helpers for + # more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Response + # https://rubydoc.info/github/rack/rack/main/Rack/Response/Helpers + class Response < Rack::Response + DROP_BODY_RESPONSES = [204, 304].freeze + + def body=(value) + value = value.body while Rack::Response === value + @body = String === value ? [value.to_str] : value + end + + def each + block_given? ? super : enum_for(:each) + end + + def finish + result = body + + if drop_content_info? + headers.delete 'content-length' + headers.delete 'content-type' + end + + if drop_body? + close + result = [] + end + + if calculate_content_length? + # if some other code has already set content-length, don't muck with it + # currently, this would be the static file-handler + headers['content-length'] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] + end + + private + + def calculate_content_length? + headers['content-type'] && !headers['content-length'] && (Array === body) + end + + def drop_content_info? + informational? || drop_body? + end + + def drop_body? + DROP_BODY_RESPONSES.include?(status) + end + end + + # Some Rack handlers implement an extended body object protocol, however, + # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question. + # This middleware will detect an extended body object and will make sure it reaches the + # handler directly. We do this here, so our middleware and middleware set up by the app will + # still be able to run. + class ExtendedRack < Struct.new(:app) + def call(env) + result = app.call(env) + callback = env['async.callback'] + return result unless callback && async?(*result) + + after_response { callback.call result } + setup_close(env, *result) + throw :async + end + + private + + def setup_close(env, _status, _headers, body) + return unless body.respond_to?(:close) && env.include?('async.close') + + env['async.close'].callback { body.close } + env['async.close'].errback { body.close } + end + + def after_response(&block) + raise NotImplementedError, 'only supports EventMachine at the moment' unless defined? EventMachine + + EventMachine.next_tick(&block) + end + + def async?(status, _headers, body) + return true if status == -1 + + body.respond_to?(:callback) && body.respond_to?(:errback) + end + end + + # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing, + # if another CommonLogger is already in the middleware chain. + class CommonLogger < Rack::CommonLogger + def call(env) + env['sinatra.commonlogger'] ? @app.call(env) : super + end + + superclass.class_eval do + alias_method :call_without_check, :call unless method_defined? :call_without_check + def call(env) + env['sinatra.commonlogger'] = true + call_without_check(env) + end + end + end + + class Error < StandardError # :nodoc: + end + + class BadRequest < Error # :nodoc: + def http_status; 400 end + end + + class NotFound < Error # :nodoc: + def http_status; 404 end + end + + # Methods available to routes, before/after filters, and views. + module Helpers + # Set or retrieve the response status code. + def status(value = nil) + response.status = Rack::Utils.status_code(value) if value + response.status + end + + # Set or retrieve the response body. When a block is given, + # evaluation is deferred until the body is read with #each. + def body(value = nil, &block) + if block_given? + def block.each; yield(call) end + response.body = block + elsif value + unless request.head? || value.is_a?(Rack::Files::BaseIterator) || value.is_a?(Stream) + headers.delete 'content-length' + end + response.body = value + else + response.body + end + end + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) + # SERVER_PROTOCOL is required in Rack 3, fall back to HTTP_VERSION + # for servers not updated for Rack 3 (like Puma 5) + http_version = env['SERVER_PROTOCOL'] || env['HTTP_VERSION'] + if (http_version == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') + status 303 + else + status 302 + end + + # According to RFC 2616 section 14.30, "the field value consists of a + # single absolute URI" + response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?) + halt(*args) + end + + # Generates the absolute URI for a given path in the app. + # Takes Rack routers and reverse proxies into account. + def uri(addr = nil, absolute = true, add_script_name = true) + return addr if addr.to_s =~ /\A[a-z][a-z0-9+.\-]*:/i + + uri = [host = String.new] + if absolute + host << "http#{'s' if request.secure?}://" + host << if request.forwarded? || (request.port != (request.secure? ? 443 : 80)) + request.host_with_port + else + request.host + end + end + uri << request.script_name.to_s if add_script_name + uri << (addr || request.path_info).to_s + File.join uri + end + + alias url uri + alias to uri + + # Halt processing and return the error status provided. + def error(code, body = nil) + if code.respond_to? :to_str + body = code.to_str + code = 500 + end + response.body = body unless body.nil? + halt code + end + + # Halt processing and return a 404 Not Found. + def not_found(body = nil) + error 404, body + end + + # Set multiple response headers with Hash. + def headers(hash = nil) + response.headers.merge! hash if hash + response.headers + end + + # Access the underlying Rack session. + def session + request.session + end + + # Access shared logger object. + def logger + request.logger + end + + # Look up a media type by file extension in Rack's mime registry. + def mime_type(type) + Base.mime_type(type) + end + + # Set the content-type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) + return response['content-type'] unless type + + default = params.delete :default + mime_type = mime_type(type) || default + raise format('Unknown media type: %p', type) if mime_type.nil? + + mime_type = mime_type.dup + unless params.include?(:charset) || settings.add_charset.all? { |p| !(p === mime_type) } + params[:charset] = params.delete('charset') || settings.default_encoding + end + params.delete :charset if mime_type.include? 'charset' + unless params.empty? + mime_type << ';' + mime_type << params.map do |key, val| + val = val.inspect if val.to_s =~ /[";,]/ + "#{key}=#{val}" + end.join(';') + end + response['content-type'] = mime_type + end + + # https://html.spec.whatwg.org/#multipart-form-data + MULTIPART_FORM_DATA_REPLACEMENT_TABLE = { + '"' => '%22', + "\r" => '%0D', + "\n" => '%0A' + }.freeze + + # Set the Content-Disposition to "attachment" with the specified filename, + # instructing the user agents to prompt to save. + def attachment(filename = nil, disposition = :attachment) + response['Content-Disposition'] = disposition.to_s.dup + return unless filename + + params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)) + response['Content-Disposition'] << params + ext = File.extname(filename) + content_type(ext) unless response['content-type'] || ext.empty? + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) + if opts[:type] || !response['content-type'] + content_type opts[:type] || File.extname(path), default: 'application/octet-stream' + end + + disposition = opts[:disposition] + filename = opts[:filename] + disposition = :attachment if disposition.nil? && filename + filename = path if filename.nil? + attachment(filename, disposition) if disposition + + last_modified opts[:last_modified] if opts[:last_modified] + + file = Rack::Files.new(File.dirname(settings.app_file)) + result = file.serving(request, path) + + result[1].each { |k, v| headers[k] ||= v } + headers['content-length'] = result[1]['content-length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT + not_found + end + + # Class of the response body in case you use #stream. + # + # Three things really matter: The front and back block (back being the + # block generating content, front the one sending it to the client) and + # the scheduler, integrating with whatever concurrency feature the Rack + # handler is using. + # + # Scheduler has to respond to defer and schedule. + class Stream + def self.schedule(*) yield end + def self.defer(*) yield end + + def initialize(scheduler = self.class, keep_open = false, &back) + @back = back.to_proc + @scheduler = scheduler + @keep_open = keep_open + @callbacks = [] + @closed = false + end + + def close + return if closed? + + @closed = true + @scheduler.schedule { @callbacks.each { |c| c.call } } + end + + def each(&front) + @front = front + @scheduler.defer do + begin + @back.call(self) + rescue Exception => e + @scheduler.schedule { raise e } + ensure + close unless @keep_open + end + end + end + + def <<(data) + @scheduler.schedule { @front.call(data.to_s) } + self + end + + def callback(&block) + return yield if closed? + + @callbacks << block + end + + alias errback callback + + def closed? + @closed + end + end + + # Allows to start sending data to the client even though later parts of + # the response body have not yet been generated. + # + # The close parameter specifies whether Stream#close should be called + # after the block has been executed. + def stream(keep_open = false) + scheduler = env['async.callback'] ? EventMachine : Stream + current = @params.dup + stream = if scheduler == Stream && keep_open + Stream.new(scheduler, false) do |out| + until out.closed? + with_params(current) { yield(out) } + end + end + else + Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } + end + body stream + end + + # Specify response freshness policy for HTTP caches (Cache-Control header). + # Any number of non-value directives (:public, :private, :no_cache, + # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with + # a Hash of value directives (:max_age, :s_maxage). + # + # cache_control :public, :must_revalidate, :max_age => 60 + # => Cache-Control: public, must-revalidate, max-age=60 + # + # See RFC 2616 / 14.9 for more on standard cache control directives: + # http://tools.ietf.org/html/rfc2616#section-14.9.1 + def cache_control(*values) + if values.last.is_a?(Hash) + hash = values.pop + hash.reject! { |_k, v| v == false } + hash.reject! { |k, v| values << k if v == true } + else + hash = {} + end + + values.map! { |value| value.to_s.tr('_', '-') } + hash.each do |key, value| + key = key.to_s.tr('_', '-') + value = value.to_i if %w[max-age s-maxage].include? key + values << "#{key}=#{value}" + end + + response['Cache-Control'] = values.join(', ') if values.any? + end + + # Set the Expires header and Cache-Control/max-age directive. Amount + # can be an integer number of seconds in the future or a Time object + # indicating when the response should be considered "stale". The remaining + # "values" arguments are passed to the #cache_control helper: + # + # expires 500, :public, :must_revalidate + # => Cache-Control: public, must-revalidate, max-age=500 + # => Expires: Mon, 08 Jun 2009 08:50:17 GMT + # + def expires(amount, *values) + values << {} unless values.last.is_a?(Hash) + + if amount.is_a? Integer + time = Time.now + amount.to_i + max_age = amount + else + time = time_for amount + max_age = time - Time.now + end + + values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 } + cache_control(*values) + + response['Expires'] = time.httpdate + end + + # Set the last modified time of the resource (HTTP 'Last-Modified' header) + # and halt if conditional GET matches. The +time+ argument is a Time, + # DateTime, or other object that responds to +to_time+. + # + # When the current request includes an 'If-Modified-Since' header that is + # equal or later than the time specified, execution is immediately halted + # with a '304 Not Modified' response. + def last_modified(time) + return unless time + + time = time_for time + response['Last-Modified'] = time.httpdate + return if env['HTTP_IF_NONE_MATCH'] + + if (status == 200) && env['HTTP_IF_MODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i + halt 304 if since >= time.to_i + end + + if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i + halt 412 if since < time.to_i + end + rescue ArgumentError + end + + ETAG_KINDS = %i[strong weak].freeze + # Set the response entity tag (HTTP 'ETag' header) and halt if conditional + # GET matches. The +value+ argument is an identifier that uniquely + # identifies the current version of the resource. The +kind+ argument + # indicates whether the etag should be used as a :strong (default) or :weak + # cache validator. + # + # When the current request includes an 'If-None-Match' header with a + # matching etag, execution is immediately halted. If the request method is + # GET or HEAD, a '304 Not Modified' response is sent. + def etag(value, options = {}) + # Before touching this code, please double check RFC 2616 14.24 and 14.26. + options = { kind: options } unless Hash === options + kind = options[:kind] || :strong + new_resource = options.fetch(:new_resource) { request.post? } + + unless ETAG_KINDS.include?(kind) + raise ArgumentError, ':strong or :weak expected' + end + + value = format('"%s"', value) + value = "W/#{value}" if kind == :weak + response['ETag'] = value + + return unless success? || status == 304 + + if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource) + halt(request.safe? ? 304 : 412) + end + + if env['HTTP_IF_MATCH'] + return if etag_matches?(env['HTTP_IF_MATCH'], new_resource) + + halt 412 + end + + nil + end + + # Sugar for redirect (example: redirect back) + def back + request.referer + end + + # whether or not the status is set to 1xx + def informational? + status.between? 100, 199 + end + + # whether or not the status is set to 2xx + def success? + status.between? 200, 299 + end + + # whether or not the status is set to 3xx + def redirect? + status.between? 300, 399 + end + + # whether or not the status is set to 4xx + def client_error? + status.between? 400, 499 + end + + # whether or not the status is set to 5xx + def server_error? + status.between? 500, 599 + end + + # whether or not the status is set to 404 + def not_found? + status == 404 + end + + # whether or not the status is set to 400 + def bad_request? + status == 400 + end + + # Generates a Time object from the given value. + # Used by #expires and #last_modified. + def time_for(value) + if value.is_a? Numeric + Time.at value + elsif value.respond_to? :to_s + Time.parse value.to_s + else + value.to_time + end + rescue ArgumentError => e + raise e + rescue Exception + raise ArgumentError, "unable to convert #{value.inspect} to a Time object" + end + + private + + # Helper method checking if a ETag value list includes the current ETag. + def etag_matches?(list, new_resource = request.post?) + return !new_resource if list == '*' + + list.to_s.split(/\s*,\s*/).include? response['ETag'] + end + + def with_params(temp_params) + original = @params + @params = temp_params + yield + ensure + @params = original if original + end + end + + # Template rendering methods. Each method takes the name of a template + # to render as a Symbol and returns a String with the rendered output, + # as well as an optional hash with additional options. + # + # `template` is either the name or path of the template as symbol + # (Use `:'subdir/myview'` for views in subdirectories), or a string + # that will be rendered. + # + # Possible options are: + # :content_type The content type to use, same arguments as content_type. + # :layout If set to something falsy, no layout is rendered, otherwise + # the specified layout is used (Ignored for `sass`) + # :layout_engine Engine to use for rendering the layout. + # :locals A hash with local variables that should be available + # in the template + # :scope If set, template is evaluate with the binding of the given + # object rather than the application instance. + # :views Views directory to use. + module Templates + module ContentTyped + attr_accessor :content_type + end + + def initialize + super + @default_layout = :layout + @preferred_extension = nil + end + + def erb(template, options = {}, locals = {}, &block) + render(:erb, template, options, locals, &block) + end + + def haml(template, options = {}, locals = {}, &block) + render(:haml, template, options, locals, &block) + end + + def sass(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :sass, template, options, locals + end + + def scss(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :scss, template, options, locals + end + + def builder(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:builder, template, options, locals, &block) + end + + def liquid(template, options = {}, locals = {}, &block) + render(:liquid, template, options, locals, &block) + end + + def markdown(template, options = {}, locals = {}) + options[:exclude_outvar] = true + render :markdown, template, options, locals + end + + def rdoc(template, options = {}, locals = {}) + render :rdoc, template, options, locals + end + + def asciidoc(template, options = {}, locals = {}) + render :asciidoc, template, options, locals + end + + def markaby(template = nil, options = {}, locals = {}, &block) + render_ruby(:mab, template, options, locals, &block) + end + + def nokogiri(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:nokogiri, template, options, locals, &block) + end + + def slim(template, options = {}, locals = {}, &block) + render(:slim, template, options, locals, &block) + end + + def yajl(template, options = {}, locals = {}) + options[:default_content_type] = :json + render :yajl, template, options, locals + end + + def rabl(template, options = {}, locals = {}) + Rabl.register! + render :rabl, template, options, locals + end + + # Calls the given block for every possible template file in views, + # named name.ext, where ext is registered on engine. + def find_template(views, name, engine) + yield ::File.join(views, "#{name}.#{@preferred_extension}") + + Tilt.default_mapping.extensions_for(engine).each do |ext| + yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension + end + end + + private + + # logic shared between builder and nokogiri + def render_ruby(engine, template, options = {}, locals = {}, &block) + if template.is_a?(Hash) + options = template + template = nil + end + template = proc { block } if template.nil? + render engine, template, options, locals + end + + def render(engine, data, options = {}, locals = {}, &block) + # merge app-level options + engine_options = settings.respond_to?(engine) ? settings.send(engine) : {} + options.merge!(engine_options) { |_key, v1, _v2| v1 } + + # extract generic options + locals = options.delete(:locals) || locals || {} + views = options.delete(:views) || settings.views || './views' + layout = options[:layout] + layout = false if layout.nil? && options.include?(:layout) + eat_errors = layout.nil? + layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false) + layout = @default_layout if layout.nil? || (layout == true) + layout_options = options.delete(:layout_options) || {} + content_type = options.delete(:default_content_type) + content_type = options.delete(:content_type) || content_type + layout_engine = options.delete(:layout_engine) || engine + scope = options.delete(:scope) || self + exclude_outvar = options.delete(:exclude_outvar) + options.delete(:layout) + + # set some defaults + options[:outvar] ||= '@_out_buf' unless exclude_outvar + options[:default_encoding] ||= settings.default_encoding + + # compile and render template + begin + layout_was = @default_layout + @default_layout = false + template = compile_template(engine, data, options, views) + output = template.render(scope, locals, &block) + ensure + @default_layout = layout_was + end + + # render layout + if layout + extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope } + options = options.merge(extra_options).merge!(layout_options) + + catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } } + end + + if content_type + # sass-embedded returns a frozen string + output = +output + output.extend(ContentTyped).content_type = content_type + end + output + end + + def compile_template(engine, data, options, views) + eat_errors = options.delete :eat_errors + template = Tilt[engine] + raise "Template engine not found: #{engine}" if template.nil? + + case data + when Symbol + template_cache.fetch engine, data, options, views do + body, path, line = settings.templates[data] + if body + body = body.call if body.respond_to?(:call) + template.new(path, line.to_i, options) { body } + else + found = false + @preferred_extension = engine.to_s + find_template(views, data, template) do |file| + path ||= file # keep the initial path rather than the last one + found = File.exist?(file) + if found + path = file + break + end + end + throw :layout_missing if eat_errors && !found + template.new(path, 1, options) + end + end + when Proc + compile_block_template(template, options, &data) + when String + template_cache.fetch engine, data, options, views do + compile_block_template(template, options) { data } + end + else + raise ArgumentError, "Sorry, don't know how to render #{data.inspect}." + end + end + + def compile_block_template(template, options, &body) + first_location = caller_locations.first + path = first_location.path + line = first_location.lineno + path = options[:path] || path + line = options[:line] || line + template.new(path, line.to_i, options, &body) + end + end + + # Extremely simple template cache implementation. + # * Not thread-safe. + # * Size is unbounded. + # * Keys are not copied defensively, and should not be modified after + # being passed to #fetch. More specifically, the values returned by + # key#hash and key#eql? should not change. + # + # Implementation copied from Tilt::Cache. + class TemplateCache + def initialize + @cache = {} + end + + # Caches a value for key, or returns the previously cached value. + # If a value has been previously cached for key then it is + # returned. Otherwise, block is yielded to and its return value + # which may be nil, is cached under key and returned. + def fetch(*key) + @cache.fetch(key) do + @cache[key] = yield + end + end + + # Clears the cache. + def clear + @cache = {} + end + end + + # Base class for all Sinatra applications and middleware. + class Base + include Rack::Utils + include Helpers + include Templates + + URI_INSTANCE = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new + + attr_accessor :app, :env, :request, :response, :params + attr_reader :template_cache + + def initialize(app = nil, **_kwargs) + super() + @app = app + @template_cache = TemplateCache.new + @pinned_response = nil # whether a before! filter pinned the content-type + yield self if block_given? + end + + # Rack call interface. + def call(env) + dup.call!(env) + end + + def call!(env) # :nodoc: + @env = env + @params = IndifferentHash.new + @request = Request.new(env) + @response = Response.new + @pinned_response = nil + template_cache.clear if settings.reload_templates + + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + + unless @response['content-type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif (default = settings.default_content_type) + content_type default + end + end + + @response.finish + end + + # Access settings defined with Base.set. + def self.settings + self + end + + # Access settings defined with Base.set. + def settings + self.class.settings + end + + # Exit the current block, halts any further processing + # of the request, and returns the specified response. + def halt(*response) + response = response.first if response.length == 1 + throw :halt, response + end + + # Pass control to the next matching route. + # If there are no more matching routes, Sinatra will + # return a 404 response. + def pass(&block) + throw :pass, block + end + + # Forward the request to the downstream app -- middleware only. + def forward + raise 'downstream app not set' unless @app.respond_to? :call + + status, headers, body = @app.call env + @response.status = status + @response.body = body + @response.headers.merge! headers + nil + end + + private + + # Run filters defined on the class and all superclasses. + # Accepts an optional block to call after each filter is applied. + def filter!(type, base = settings, &block) + filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters) + base.filters[type].each do |args| + result = process_route(*args) + block.call(result) if block_given? + end + end + + # Run routes defined on the class and all superclasses. + def route!(base = settings, pass_block = nil) + routes = base.routes[@request.request_method] + + routes&.each do |pattern, conditions, block| + response.delete_header('content-type') unless @pinned_response + + returned_pass_block = process_route(pattern, conditions) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" + route_eval { block[*args] } + end + + # don't wipe out pass_block in superclass + pass_block = returned_pass_block if returned_pass_block + end + + # Run routes defined in superclass. + if base.superclass.respond_to?(:routes) + return route!(base.superclass, pass_block) + end + + route_eval(&pass_block) if pass_block + route_missing + end + + # Run a route block and throw :halt with the result. + def route_eval + throw :halt, yield + end + + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, conditions, block = nil, values = []) + route = @request.path_info + route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/') + + params = pattern.params(route) + return unless params + + params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes + force_encoding(params) + @params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any? + + regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) }) + if regexp_exists + captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c } + values += captures + @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty? + else + values += params.values.flatten + end + + catch(:pass) do + conditions.each { |c| throw :pass if c.bind(self).call == false } + block ? block[self, values] : yield(self, values) + end + rescue StandardError + @env['sinatra.error.params'] = @params + raise + ensure + params ||= {} + params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params'] + end + + # No matching route was found or all routes passed. The default + # implementation is to forward the request downstream when running + # as middleware (@app is non-nil); when no downstream app is set, raise + # a NotFound exception. Subclasses can override this method to perform + # custom route miss logic. + def route_missing + raise NotFound unless @app + + forward + end + + # Attempt to serve static files from public directory. Throws :halt when + # a matching file is found, returns nil otherwise. + # If custom static headers are defined, use them. + def static!(options = {}) + return if (public_dir = settings.public_folder).nil? + + path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" + return unless valid_path?(path) + + path = File.expand_path(path) + return unless path.start_with?("#{File.expand_path(public_dir)}/") + + return unless File.file?(path) + + env['sinatra.static_file'] = path + cache_control(*settings.static_cache_control) if settings.static_cache_control? + + headers(settings.static_headers) if settings.static_headers? + + send_file path, options.merge(disposition: nil) + end + + # Run the block with 'throw :halt' support and apply result to the response. + def invoke(&block) + res = catch(:halt, &block) + + res = [res] if (Integer === res) || (String === res) + if (Array === res) && (Integer === res.first) + res = res.dup + status(res.shift) + body(res.pop) + headers(*res) + elsif res.respond_to? :each + body res + end + nil # avoid double setting the same response tuple twice + end + + # Dispatch a request with error handling. + def dispatch! + # Avoid passing frozen string in force_encoding + @params.merge!(@request.params).each do |key, val| + next unless val.respond_to?(:force_encoding) + + val = val.dup if val.frozen? + @params[key] = force_encoding(val) + end + + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do + @pinned_response = !response['content-type'].nil? + end + route! + end + rescue ::Exception => e + invoke { handle_exception!(e) } + ensure + begin + filter! :after unless env['sinatra.static_file'] + rescue ::Exception => e + invoke { handle_exception!(e) } unless @env['sinatra.error'] + end + end + + # Error handling during requests. + def handle_exception!(boom) + error_params = @env['sinatra.error.params'] + + @params = @params.merge(error_params) if error_params + + @env['sinatra.error'] = boom + + http_status = if boom.is_a? Sinatra::Error + if boom.respond_to? :http_status + boom.http_status + elsif settings.use_code? && boom.respond_to?(:code) + boom.code + end + end + + http_status = 500 unless http_status&.between?(400, 599) + status(http_status) + + if server_error? + dump_errors! boom if settings.dump_errors? + raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler) + elsif not_found? + headers['X-Cascade'] = 'pass' if settings.x_cascade? + end + + if (res = error_block!(boom.class, boom) || error_block!(status, boom)) + return res + end + + if not_found? || bad_request? + if boom.message && boom.message != boom.class.name + body Rack::Utils.escape_html(boom.message) + else + content_type 'text/html' + body "

#{not_found? ? 'Not Found' : 'Bad Request'}

" + end + end + + return unless server_error? + + raise boom if settings.raise_errors? || settings.show_exceptions? + + error_block! Exception, boom + end + + # Find an custom error block for the key(s) specified. + def error_block!(key, *block_params) + base = settings + while base.respond_to?(:errors) + args_array = base.errors[key] + + next base = base.superclass unless args_array + + args_array.reverse_each do |args| + first = args == args_array.first + args += [block_params] + resp = process_route(*args) + return resp unless resp.nil? && !first + end + end + return false unless key.respond_to?(:superclass) && (key.superclass < Exception) + + error_block!(key.superclass, *block_params) + end + + def dump_errors!(boom) + if boom.respond_to?(:detailed_message) + msg = boom.detailed_message(highlight: false) + if msg =~ /\A(.*?)(?: \(#{ Regexp.quote(boom.class.to_s) }\))?\n/ + msg = $1 + additional_msg = $'.lines(chomp: true) + else + additional_msg = [] + end + else + msg = boom.message + additional_msg = [] + end + msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{msg}:", *additional_msg, *boom.backtrace].join("\n\t") + @env['rack.errors'].puts(msg) + end + + class << self + CALLERS_TO_IGNORE = [ # :nodoc: + %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code + %r{lib/tilt.*\.rb$}, # all tilt code + /^\(.*\)$/, # generated code + /\/bundled_gems.rb$/, # ruby >= 3.3 with bundler >= 2.5 + %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks + /active_support/, # active_support require hacks + %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks + /= 1.9.2 + %r{zeitwerk/(core_ext/)?kernel\.rb} # Zeitwerk kernel#require decorator + ].freeze + + attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback + + def callers_to_ignore + CALLERS_TO_IGNORE + end + + # Removes all routes, filters, middleware and extension hooks from the + # current class (not routes/filters/... defined by its superclass). + def reset! + @conditions = [] + @routes = {} + @filters = { before: [], after: [] } + @errors = {} + @middleware = [] + @prototype = nil + @extensions = [] + + @templates = if superclass.respond_to?(:templates) + Hash.new { |_hash, key| superclass.templates[key] } + else + {} + end + end + + # Extension modules registered on this class and all superclasses. + def extensions + if superclass.respond_to?(:extensions) + (@extensions + superclass.extensions).uniq + else + @extensions + end + end + + # Middleware used in this class and all superclasses. + def middleware + if superclass.respond_to?(:middleware) + superclass.middleware + @middleware + else + @middleware + end + end + + # Sets an option to the given value. If the value is a proc, + # the proc will be called every time the option is accessed. + def set(option, value = (not_set = true), ignore_setter = false, &block) + raise ArgumentError if block && !not_set + + if block + value = block + not_set = false + end + + if not_set + raise ArgumentError unless option.respond_to?(:each) + + option.each { |k, v| set(k, v) } + return self + end + + if respond_to?("#{option}=") && !ignore_setter + return __send__("#{option}=", value) + end + + setter = proc { |val| set option, val, true } + getter = proc { value } + + case value + when Proc + getter = value + when Symbol, Integer, FalseClass, TrueClass, NilClass + getter = value.inspect + when Hash + setter = proc do |val| + val = value.merge val if Hash === val + set option, val, true + end + end + + define_singleton("#{option}=", setter) + define_singleton(option, getter) + define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?" + self + end + + # Same as calling `set :option, true` for each of the given options. + def enable(*opts) + opts.each { |key| set(key, true) } + end + + # Same as calling `set :option, false` for each of the given options. + def disable(*opts) + opts.each { |key| set(key, false) } + end + + # Define a custom error handler. Optionally takes either an Exception + # class, or an HTTP status code to specify which errors should be + # handled. + def error(*codes, &block) + args = compile! 'ERROR', /.*/, block + codes = codes.flat_map(&method(:Array)) + codes << Exception if codes.empty? + codes << Sinatra::NotFound if codes.include?(404) + codes.each { |c| (@errors[c] ||= []) << args } + end + + # Sugar for `error(404) { ... }` + def not_found(&block) + error(404, &block) + end + + # Define a named template. The block must return the template source. + def template(name, &block) + filename, line = caller_locations.first + templates[name] = [block, filename, line.to_i] + end + + # Define the layout template. The block must return the template source. + def layout(name = :layout, &block) + template name, &block + end + + # Load embedded templates from the file; uses the caller's __FILE__ + # when no file is specified. + def inline_templates=(file = nil) + file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true + + begin + io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file) + app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2) + rescue Errno::ENOENT + app, data = nil + end + + return unless data + + encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m + $2 + else + settings.default_encoding + end + + lines = app.count("\n") + 1 + template = nil + force_encoding data, encoding + data.each_line do |line| + lines += 1 + if line =~ /^@@\s*(.*\S)\s*$/ + template = force_encoding(String.new, encoding) + templates[$1.to_sym] = [template, file, lines] + elsif template + template << line + end + end + end + + # Lookup or register a mime type in Rack's mime registry. + def mime_type(type, value = nil) + return type if type.nil? + return type.to_s if type.to_s.include?('/') + + type = ".#{type}" unless type.to_s[0] == '.' + return Rack::Mime.mime_type(type, nil) unless value + + Rack::Mime::MIME_TYPES[type] = value + end + + # provides all mime types matching type, including deprecated types: + # mime_types :html # => ['text/html'] + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type + if type =~ %r{^application/(xml|javascript)$} + [type, "text/#{$1}"] + elsif type =~ %r{^text/(xml|javascript)$} + [type, "application/#{$1}"] + else + [type] + end + end + + # Define a before filter; runs before all requests within the same + # context as route handlers and may access/modify the request and + # response. + def before(path = /.*/, **options, &block) + add_filter(:before, path, **options, &block) + end + + # Define an after filter; runs after all requests within the same + # context as route handlers and may access/modify the request and + # response. + def after(path = /.*/, **options, &block) + add_filter(:after, path, **options, &block) + end + + # add a filter + def add_filter(type, path = /.*/, **options, &block) + filters[type] << compile!(type, path, block, **options) + end + + def on_start(&on_start_callback) + @on_start_callback = on_start_callback + end + + def on_stop(&on_stop_callback) + @on_stop_callback = on_stop_callback + end + + # Add a route condition. The route is considered non-matching when the + # block returns false. + def condition(name = "#{caller.first[/`.*'/]} condition", &block) + @conditions << generate_method(name, &block) + end + + def public=(value) + warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead' + set(:public_folder, value) + end + + def public_dir=(value) + self.public_folder = value + end + + def public_dir + public_folder + end + + # Defining a `GET` handler also automatically defines + # a `HEAD` handler. + def get(path, opts = {}, &block) + conditions = @conditions.dup + route('GET', path, opts, &block) + + @conditions = conditions + route('HEAD', path, opts, &block) + end + + def put(path, opts = {}, &block) route 'PUT', path, opts, &block end + + def post(path, opts = {}, &block) route 'POST', path, opts, &block end + + def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end + + def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end + + def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end + + def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end + + def link(path, opts = {}, &block) route 'LINK', path, opts, &block end + + def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end + + # Makes the methods defined in the block and in the Modules given + # in `extensions` available to the handlers and templates + def helpers(*extensions, &block) + class_eval(&block) if block_given? + include(*extensions) if extensions.any? + end + + # Register an extension. Alternatively take a block from which an + # extension will be created and registered on the fly. + def register(*extensions, &block) + extensions << Module.new(&block) if block_given? + @extensions += extensions + extensions.each do |extension| + extend extension + extension.registered(self) if extension.respond_to?(:registered) + end + end + + def development?; environment == :development end + def production?; environment == :production end + def test?; environment == :test end + + # Set configuration options for Sinatra and/or the app. + # Allows scoping of settings for certain environments. + def configure(*envs) + yield self if envs.empty? || envs.include?(environment.to_sym) + end + + # Use the specified Rack middleware + def use(middleware, *args, &block) + @prototype = nil + @middleware << [middleware, args, block] + end + ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true) + + # Stop the self-hosted server if running. + def quit! + return unless running? + + # Use Thin's hard #stop! if available, otherwise just #stop. + running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop + warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages? + set :running_server, nil + set :handler_name, nil + + on_stop_callback.call unless on_stop_callback.nil? + end + + alias stop! quit! + + # Run the Sinatra app as a self-hosted server using + # Puma, Falcon (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + unless defined?(Rackup::Handler) + rackup_warning = <<~MISSING_RACKUP + Sinatra could not start, the required gems weren't found! + + Add them to your bundle with: + + bundle add rackup puma + + or install them with: + + gem install rackup puma + + MISSING_RACKUP + warn rackup_warning + exit 1 + end + + return if running? + + set options + handler = Rackup::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(Port: port, Host: bind) + + begin + start_server(handler, server_settings, handler_name, &block) + rescue Errno::EADDRINUSE + warn "== Someone is already performing on port #{port}!" + raise + ensure + quit! + end + end + + alias start! run! + + # Check whether the self-hosted server is running or not. + def running? + running_server? + end + + # The prototype instance used to process requests. + def prototype + @prototype ||= new + end + + # Create a new instance without middleware in front of it. + alias new! new unless method_defined? :new! + + # Create a new instance of the class fronted by its middleware + # pipeline. The object is guaranteed to respond to #call but may not be + # an instance of the class new was called on. + def new(*args, &block) + instance = new!(*args, &block) + Wrapper.new(build(instance).to_app, instance) + end + ruby2_keywords :new if respond_to?(:ruby2_keywords, true) + + # Creates a Rack::Builder instance with all the middleware set up and + # the given +app+ as end point. + def build(app) + builder = Rack::Builder.new + setup_default_middleware builder + setup_middleware builder + builder.run app + builder + end + + def call(env) + synchronize { prototype.call(env) } + end + + # Like Kernel#caller but excluding certain magic entries and without + # line / method information; the resulting array contains filenames only. + def caller_files + cleaned_caller(1).flatten + end + + private + + # Starts the server by running the Rack Handler. + def start_server(handler, server_settings, handler_name) + # Ensure we initialize middleware before startup, to match standard Rack + # behavior, by ensuring an instance exists: + prototype + # Run the instance we created: + handler.run(self, **server_settings) do |server| + unless suppress_messages? + warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}" + end + + setup_traps + set :running_server, server + set :handler_name, handler_name + server.threaded = settings.threaded if server.respond_to? :threaded= + on_start_callback.call unless on_start_callback.nil? + yield server if block_given? + end + end + + def suppress_messages? + handler_name =~ /cgi/i || quiet + end + + def setup_traps + return unless traps? + + at_exit { quit! } + + %i[INT TERM].each do |signal| + old_handler = trap(signal) do + quit! + old_handler.call if old_handler.respond_to?(:call) + end + end + + set :traps, false + end + + # Dynamically defines a method on settings. + def define_singleton(name, content = Proc.new) + singleton_class.class_eval do + undef_method(name) if method_defined? name + String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content) + end + end + + # Condition for matching host name. Parameter might be String or Regexp. + def host_name(pattern) + condition { pattern === request.host } + end + + # Condition for matching user agent. Parameter should be Regexp. + # Will set params[:agent]. + def user_agent(pattern) + condition do + if request.user_agent.to_s =~ pattern + @params[:agent] = $~[1..-1] + true + else + false + end + end + end + alias agent user_agent + + # Condition for matching mimetypes. Accepts file extensions. + def provides(*types) + types.map! { |t| mime_types(t) } + types.flatten! + condition do + response_content_type = response['content-type'] + preferred_type = request.preferred_type(types) + + if response_content_type + types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/]) + elsif preferred_type + params = (preferred_type.respond_to?(:params) ? preferred_type.params : {}) + content_type(preferred_type, params) + true + else + false + end + end + end + + def route(verb, path, options = {}, &block) + signature = compile!(verb, path, block, **options) + (@routes[verb] ||= []) << signature + invoke_hook(:route_added, verb, path, block) + signature + end + + def invoke_hook(name, *args) + extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } + end + + def generate_method(method_name, &block) + define_method(method_name, &block) + method = instance_method method_name + remove_method method_name + method + end + + def compile!(verb, path, block, **options) + # Because of self.options.host + host_name(options.delete(:host)) if options.key?(:host) + # Pass Mustermann opts to compile() + route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze + + options.each_pair { |option, args| send(option, *args) } + + pattern = compile(path, route_mustermann_opts) + method_name = "#{verb} #{path}" + unbound_method = generate_method(method_name, &block) + conditions = @conditions + @conditions = [] + wrapper = block.arity.zero? ? + proc { |a, _p| unbound_method.bind(a).call } : + proc { |a, p| unbound_method.bind(a).call(*p) } + + [pattern, conditions, wrapper] + end + + def compile(path, route_mustermann_opts = {}) + Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts)) + end + + def setup_default_middleware(builder) + builder.use ExtendedRack + builder.use ShowExceptions if show_exceptions? + builder.use Rack::MethodOverride if method_override? + builder.use Rack::Head + setup_logging builder + setup_sessions builder + setup_protection builder + setup_host_authorization builder + end + + def setup_middleware(builder) + middleware.each { |c, a, b| builder.use(c, *a, &b) } + end + + def setup_logging(builder) + if logging? + setup_common_logger(builder) + setup_custom_logger(builder) + elsif logging == false + setup_null_logger(builder) + end + end + + def setup_null_logger(builder) + builder.use Sinatra::Middleware::Logger, ::Logger::FATAL + end + + def setup_common_logger(builder) + builder.use Sinatra::CommonLogger + end + + def setup_custom_logger(builder) + if logging.respond_to? :to_int + builder.use Sinatra::Middleware::Logger, logging + else + builder.use Sinatra::Middleware::Logger + end + end + + def setup_protection(builder) + return unless protection? + + options = Hash === protection ? protection.dup : {} + options = { + img_src: "'self' data:", + font_src: "'self'" + }.merge options + + protect_session = options.fetch(:session) { sessions? } + options[:without_session] = !protect_session + + options[:reaction] ||= :drop_session + + builder.use Rack::Protection, options + end + + def setup_host_authorization(builder) + builder.use Rack::Protection::HostAuthorization, host_authorization + end + + def setup_sessions(builder) + return unless sessions? + + options = {} + options[:secret] = session_secret if session_secret? + options.merge! sessions.to_hash if sessions.respond_to? :to_hash + builder.use session_store, options + end + + def inherited(subclass) + subclass.reset! + subclass.set :app_file, caller_files.first unless subclass.app_file? + super + end + + @@mutex = Mutex.new + def synchronize(&block) + if lock? + @@mutex.synchronize(&block) + else + yield + end + end + + # used for deprecation warnings + def warn_for_deprecation(message) + warn message + "\n\tfrom #{cleaned_caller.first.join(':')}" + end + + # Like Kernel#caller but excluding certain magic entries + def cleaned_caller(keep = 3) + caller(1) + .map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] } + .reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } } + end + end + + # Force data to specified encoding. It defaults to settings.default_encoding + # which is UTF-8 by default + def self.force_encoding(data, encoding = default_encoding) + return if data == settings || data.is_a?(Tempfile) + + if data.respond_to? :force_encoding + data.force_encoding(encoding).encode! + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v, encoding) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v, encoding) } + end + data + end + + def force_encoding(*args) + settings.force_encoding(*args) + end + + reset! + + set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym + set :raise_errors, proc { test? } + set :dump_errors, proc { !test? } + set :show_exceptions, proc { development? } + set :sessions, false + set :session_store, Rack::Session::Cookie + set :logging, false + set :protection, true + set :method_override, false + set :use_code, false + set :default_encoding, 'utf-8' + set :x_cascade, true + set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" } + settings.add_charset << %r{^text/} + set :mustermann_opts, {} + set :default_content_type, 'text/html' + + # explicitly generating a session secret eagerly to play nice with preforking + begin + require 'securerandom' + set :session_secret, SecureRandom.hex(64) + rescue LoadError, NotImplementedError, RuntimeError + # SecureRandom raises a NotImplementedError if no random device is available + # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230 + set :session_secret, format('%064x', Kernel.rand((2**256) - 1)) + end + + class << self + alias methodoverride? method_override? + alias methodoverride= method_override= + end + + set :run, false # start server via at-exit hook? + set :running_server, nil + set :handler_name, nil + set :traps, true + set :server, %w[webrick] + set :bind, proc { development? ? 'localhost' : '0.0.0.0' } + set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567) + set :quiet, false + set :host_authorization, ->() do + if development? + { + permitted_hosts: [ + "localhost", + ".localhost", + ".test", + IPAddr.new("0.0.0.0/0"), + IPAddr.new("::/0"), + ] + } + else + {} + end + end + + ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'falcon' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + server.unshift 'puma' + + set :absolute_redirects, true + set :prefixed_redirects, false + set :strict_paths, true + + set :app_file, nil + set :root, proc { app_file && File.expand_path(File.dirname(app_file)) } + set :views, proc { root && File.join(root, 'views') } + set :reload_templates, proc { development? } + set :lock, false + set :threaded, true + + set :public_folder, proc { root && File.join(root, 'public') } + set :static, proc { public_folder && File.exist?(public_folder) } + set :static_cache_control, false + + set :static_headers, {} + + error ::Exception do + response.status = 500 + content_type 'text/html' + '

Internal Server Error

' + end + + configure :development do + get '/__sinatra__/:image.png' do + filename = __dir__ + "/images/#{params[:image].to_i}.png" + content_type :png + send_file filename + end + + error NotFound do + content_type 'text/html' + + if instance_of?(Sinatra::Application) + code = <<-RUBY.gsub(/^ {12}/, '') + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + RUBY + else + code = <<-RUBY.gsub(/^ {12}/, '') + class #{self.class} + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + end + RUBY + + file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '') + code = "# in #{file}\n#{code}" unless file.empty? + end + + <<-HTML.gsub(/^ {10}/, '') + + + + + + +

Sinatra doesn’t know this ditty.

+ +
+ Try this: +
#{Rack::Utils.escape_html(code)}
+
+ + + HTML + end + end + end + + # Execution context for classic style (top-level) applications. All + # DSL methods executed on main are delegated to this class. + # + # The Application class should not be subclassed, unless you want to + # inherit all settings, routes, handlers, and error pages from the + # top-level. Subclassing Sinatra::Base is highly recommended for + # modular applications. + class Application < Base + set :logging, proc { !test? } + set :method_override, true + set :run, proc { !test? } + set :app_file, nil + + def self.register(*extensions, &block) # :nodoc: + added_methods = extensions.flat_map(&:public_instance_methods) + Delegator.delegate(*added_methods) + super(*extensions, &block) + end + end + + # Sinatra delegation mixin. Mixing this module into an object causes all + # methods to be delegated to the Sinatra::Application class. Used primarily + # at the top-level. + module Delegator # :nodoc: + def self.delegate(*methods) + methods.each do |method_name| + define_method(method_name) do |*args, &block| + return super(*args, &block) if respond_to? method_name + + Delegator.target.send(method_name, *args, &block) + end + # ensure keyword argument passing is compatible with ruby >= 2.7 + ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true) + private method_name + end + end + + delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink, + :template, :layout, :before, :after, :error, :not_found, :configure, + :set, :mime_type, :enable, :disable, :use, :development?, :test?, + :production?, :helpers, :settings, :register, :on_start, :on_stop + + class << self + attr_accessor :target + end + + self.target = Application + end + + class Wrapper + def initialize(stack, instance) + @stack = stack + @instance = instance + end + + def settings + @instance.settings + end + + def helpers + @instance + end + + def call(env) + @stack.call(env) + end + + def inspect + "#<#{@instance.class} app_file=#{settings.app_file.inspect}>" + end + end + + # Create a new Sinatra application; the block is evaluated in the class scope. + def self.new(base = Base, &block) + base = Class.new(base) + base.class_eval(&block) if block_given? + base + end + + # Extend the top-level DSL with the modules provided. + def self.register(*extensions, &block) + Delegator.target.register(*extensions, &block) + end + + # Include the helper modules provided in Sinatra's request context. + def self.helpers(*extensions, &block) + Delegator.target.helpers(*extensions, &block) + end + + # Use the middleware for classic applications. + def self.use(*args, &block) + Delegator.target.use(*args, &block) + end +end diff --git a/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/expected.json b/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/expected.json new file mode 100644 index 0000000..9e39e86 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/expected.json @@ -0,0 +1,1354 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 6, + 3, + 3, + 2, + 0, + 2, + 2, + 0 + ], + "type": "hash", + "start_byte": 782, + "end_byte": 796 + }, + "parent": { + "tree": "after", + "path": [ + 6, + 3, + 3, + 2, + 0, + 2, + 2 + ], + "type": "argument_list", + "start_byte": 781, + "end_byte": 797 + }, + "position": 0 + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 6, + 3, + 3, + 2, + 0, + 2, + 2, + 0 + ], + "type": "pair", + "start_byte": 782, + "end_byte": 787 + }, + "parent": { + "tree": "after", + "path": [ + 6, + 3, + 3, + 2, + 0, + 2, + 2, + 0 + ], + "type": "hash", + "start_byte": 782, + "end_byte": 796 + }, + "position": 0, + "old_parent": { + "tree": "before", + "path": [ + 6, + 3, + 3, + 2, + 0, + 2, + 2 + ], + "type": "argument_list", + "start_byte": 781, + "end_byte": 795 + }, + "old_position": 0, + "subtree": true, + "dest_start_byte": 783, + "dest_end_byte": 788, + "group_id": "group-1" + }, + { + "action": "move", + "node": { + "tree": "before", + "path": [ + 6, + 3, + 3, + 2, + 0, + 2, + 2, + 1 + ], + "type": "pair", + "start_byte": 789, + "end_byte": 794 + }, + "parent": { + "tree": "after", + "path": [ + 6, + 3, + 3, + 2, + 0, + 2, + 2, + 0 + ], + "type": "hash", + "start_byte": 782, + "end_byte": 796 + }, + "position": 1, + "old_parent": { + "tree": "before", + "path": [ + 6, + 3, + 3, + 2, + 0, + 2, + 2 + ], + "type": "argument_list", + "start_byte": 781, + "end_byte": 795 + }, + "old_position": 1, + "subtree": true, + "dest_start_byte": 790, + "dest_end_byte": 795, + "group_id": "group-1" + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": 1, + "right_line": 1 + }, + { + "left_line": 2, + "right_line": 2 + }, + { + "left_line": 3, + "right_line": 3 + }, + { + "left_line": 4, + "right_line": 4 + }, + { + "left_line": 5, + "right_line": 5 + }, + { + "left_line": 6, + "right_line": 6 + }, + { + "left_line": 7, + "right_line": 7 + }, + { + "left_line": 8, + "right_line": 8 + }, + { + "left_line": 9, + "right_line": 9 + }, + { + "left_line": 10, + "right_line": 10 + }, + { + "left_line": 11, + "right_line": 11 + }, + { + "left_line": 12, + "right_line": 12 + }, + { + "left_line": 13, + "right_line": 13 + }, + { + "left_line": 14, + "right_line": 14 + }, + { + "left_line": 15, + "right_line": 15 + }, + { + "left_line": 16, + "right_line": 16 + }, + { + "left_line": 17, + "right_line": 17 + }, + { + "left_line": 18, + "right_line": 18 + }, + { + "left_line": 19, + "right_line": 19 + }, + { + "left_line": 20, + "right_line": 20 + }, + { + "left_line": 21, + "right_line": 21 + }, + { + "left_line": 22, + "right_line": 22 + }, + { + "left_line": 23, + "right_line": 23 + }, + { + "left_line": 24, + "right_line": 24 + }, + { + "left_line": 25, + "right_line": 25 + }, + { + "left_line": 26, + "right_line": 26 + }, + { + "left_line": 27, + "right_line": 27 + }, + { + "left_line": 28, + "right_line": 28 + }, + { + "left_line": 29, + "right_line": 29 + }, + { + "left_line": 30, + "right_line": 30 + }, + { + "left_line": 31, + "right_line": 31 + }, + { + "left_line": 32, + "right_line": 32 + }, + { + "left_line": 33, + "right_line": 33 + }, + { + "left_line": 34, + "right_line": 34 + }, + { + "left_line": 35, + "right_line": 35 + }, + { + "left_line": 36, + "right_line": 36 + }, + { + "left_line": 37, + "right_line": 37 + }, + { + "left_line": 38, + "right_line": 38 + }, + { + "left_line": 39, + "right_line": 39 + }, + { + "left_line": 40, + "right_line": 40 + }, + { + "left_line": 41, + "right_line": 41 + }, + { + "left_line": 42, + "right_line": 42 + }, + { + "left_line": 43, + "right_line": 43 + }, + { + "left_line": 44, + "right_line": 44 + }, + { + "left_line": 45, + "right_line": 45 + }, + { + "left_line": 46, + "right_line": 46 + }, + { + "left_line": 47, + "right_line": 47 + }, + { + "left_line": 48, + "right_line": 48 + }, + { + "left_line": 49, + "right_line": 49 + }, + { + "left_line": 50, + "right_line": 50 + }, + { + "left_line": 51, + "right_line": 51 + }, + { + "left_line": 52, + "right_line": 52 + }, + { + "left_line": 53, + "right_line": 53 + }, + { + "left_line": 54, + "right_line": 54 + }, + { + "left_line": 55, + "right_line": 55 + }, + { + "left_line": 56, + "right_line": 56 + }, + { + "left_line": 57, + "right_line": 57 + }, + { + "left_line": 58, + "right_line": 58 + }, + { + "left_line": 59, + "right_line": 59 + }, + { + "left_line": 60, + "right_line": 60 + }, + { + "left_line": 61, + "right_line": 61 + }, + { + "left_line": 62, + "right_line": 62 + }, + { + "left_line": 63, + "right_line": 63 + }, + { + "left_line": 64, + "right_line": 64 + }, + { + "left_line": 65, + "right_line": 65 + }, + { + "left_line": 66, + "right_line": 66 + }, + { + "left_line": 67, + "right_line": 67 + }, + { + "left_line": 68, + "right_line": 68 + }, + { + "left_line": 69, + "right_line": 69 + }, + { + "left_line": 70, + "right_line": 70 + }, + { + "left_line": 71, + "right_line": 71 + }, + { + "left_line": 72, + "right_line": 72 + }, + { + "left_line": 73, + "right_line": 73 + }, + { + "left_line": 74, + "right_line": 74 + }, + { + "left_line": 75, + "right_line": 75 + }, + { + "left_line": 76, + "right_line": 76 + }, + { + "left_line": 77, + "right_line": 77 + }, + { + "left_line": 78, + "right_line": 78 + }, + { + "left_line": 79, + "right_line": 79 + }, + { + "left_line": 80, + "right_line": 80 + }, + { + "left_line": 81, + "right_line": 81 + }, + { + "left_line": 82, + "right_line": 82 + }, + { + "left_line": 83, + "right_line": 83 + }, + { + "left_line": 84, + "right_line": 84 + }, + { + "left_line": 85, + "right_line": 85 + }, + { + "left_line": 86, + "right_line": 86 + }, + { + "left_line": 87, + "right_line": 87 + }, + { + "left_line": 88, + "right_line": 88 + }, + { + "left_line": 89, + "right_line": 89 + }, + { + "left_line": 90, + "right_line": 90 + }, + { + "left_line": 91, + "right_line": 91 + }, + { + "left_line": 92, + "right_line": 92 + }, + { + "left_line": 93, + "right_line": 93 + }, + { + "left_line": 94, + "right_line": 94 + }, + { + "left_line": 95, + "right_line": 95 + }, + { + "left_line": 96, + "right_line": 96 + }, + { + "left_line": 97, + "right_line": 97 + }, + { + "left_line": 98, + "right_line": 98 + }, + { + "left_line": 99, + "right_line": 99 + }, + { + "left_line": 100, + "right_line": 100 + }, + { + "left_line": 101, + "right_line": 101 + }, + { + "left_line": 102, + "right_line": 102 + }, + { + "left_line": 103, + "right_line": 103 + }, + { + "left_line": 104, + "right_line": 104 + }, + { + "left_line": 105, + "right_line": 105 + }, + { + "left_line": 106, + "right_line": 106 + }, + { + "left_line": 107, + "right_line": 107 + }, + { + "left_line": 108, + "right_line": 108 + }, + { + "left_line": 109, + "right_line": 109 + }, + { + "left_line": 110, + "right_line": 110 + }, + { + "left_line": 111, + "right_line": 111 + }, + { + "left_line": 112, + "right_line": 112 + }, + { + "left_line": 113, + "right_line": 113 + }, + { + "left_line": 114, + "right_line": 114 + }, + { + "left_line": 115, + "right_line": 115 + }, + { + "left_line": 116, + "right_line": 116 + }, + { + "left_line": 117, + "right_line": 117 + }, + { + "left_line": 118, + "right_line": 118 + }, + { + "left_line": 119, + "right_line": 119 + }, + { + "left_line": 120, + "right_line": 120 + }, + { + "left_line": 121, + "right_line": 121 + }, + { + "left_line": 122, + "right_line": 122 + }, + { + "left_line": 123, + "right_line": 123 + }, + { + "left_line": 124, + "right_line": 124 + }, + { + "left_line": 125, + "right_line": 125 + }, + { + "left_line": 126, + "right_line": 126 + }, + { + "left_line": 127, + "right_line": 127 + }, + { + "left_line": 128, + "right_line": 128 + }, + { + "left_line": 129, + "right_line": 129 + }, + { + "left_line": 130, + "right_line": 130 + }, + { + "left_line": 131, + "right_line": 131 + }, + { + "left_line": 132, + "right_line": 132 + }, + { + "left_line": 133, + "right_line": 133 + }, + { + "left_line": 134, + "right_line": 134 + }, + { + "left_line": 135, + "right_line": 135 + }, + { + "left_line": 136, + "right_line": 136 + }, + { + "left_line": 137, + "right_line": 137 + }, + { + "left_line": 138, + "right_line": 138 + }, + { + "left_line": 139, + "right_line": 139 + }, + { + "left_line": 140, + "right_line": 140 + }, + { + "left_line": 141, + "right_line": 141 + }, + { + "left_line": 142, + "right_line": 142 + }, + { + "left_line": 143, + "right_line": 143 + }, + { + "left_line": 144, + "right_line": 144 + }, + { + "left_line": 145, + "right_line": 145 + }, + { + "left_line": 146, + "right_line": 146 + }, + { + "left_line": 147, + "right_line": 147 + }, + { + "left_line": 148, + "right_line": 148 + }, + { + "left_line": 149, + "right_line": 149 + }, + { + "left_line": 150, + "right_line": 150 + }, + { + "left_line": 151, + "right_line": 151 + }, + { + "left_line": 152, + "right_line": 152 + }, + { + "left_line": 153, + "right_line": 153 + }, + { + "left_line": 154, + "right_line": 154 + }, + { + "left_line": 155, + "right_line": 155 + }, + { + "left_line": 156, + "right_line": 156 + }, + { + "left_line": 157, + "right_line": 157 + }, + { + "left_line": 158, + "right_line": 158 + }, + { + "left_line": 159, + "right_line": 159 + }, + { + "left_line": 160, + "right_line": 160 + }, + { + "left_line": 161, + "right_line": 161 + }, + { + "left_line": 162, + "right_line": 162 + }, + { + "left_line": 163, + "right_line": 163 + }, + { + "left_line": 164, + "right_line": 164 + }, + { + "left_line": 165, + "right_line": 165 + }, + { + "left_line": 166, + "right_line": 166 + }, + { + "left_line": 167, + "right_line": 167 + }, + { + "left_line": 168, + "right_line": 168 + }, + { + "left_line": 169, + "right_line": 169 + }, + { + "left_line": 170, + "right_line": 170 + }, + { + "left_line": 171, + "right_line": 171 + }, + { + "left_line": 172, + "right_line": 172 + }, + { + "left_line": 173, + "right_line": 173 + }, + { + "left_line": 174, + "right_line": 174 + }, + { + "left_line": 175, + "right_line": 175 + }, + { + "left_line": 176, + "right_line": 176 + }, + { + "left_line": 177, + "right_line": 177 + }, + { + "left_line": 178, + "right_line": 178 + }, + { + "left_line": 179, + "right_line": 179 + }, + { + "left_line": 180, + "right_line": 180 + }, + { + "left_line": 181, + "right_line": 181 + }, + { + "left_line": 182, + "right_line": 182 + }, + { + "left_line": 183, + "right_line": 183 + }, + { + "left_line": 184, + "right_line": 184 + }, + { + "left_line": 185, + "right_line": 185 + }, + { + "left_line": 186, + "right_line": 186 + }, + { + "left_line": 187, + "right_line": 187 + }, + { + "left_line": 188, + "right_line": 188 + }, + { + "left_line": 189, + "right_line": 189 + }, + { + "left_line": 190, + "right_line": 190 + }, + { + "left_line": 191, + "right_line": 191 + }, + { + "left_line": 192, + "right_line": 192 + }, + { + "left_line": 193, + "right_line": 193 + }, + { + "left_line": 194, + "right_line": 194 + }, + { + "left_line": 195, + "right_line": 195 + }, + { + "left_line": 196, + "right_line": 196 + }, + { + "left_line": 197, + "right_line": 197 + }, + { + "left_line": 198, + "right_line": 198 + }, + { + "left_line": 199, + "right_line": 199 + }, + { + "left_line": 200, + "right_line": 200 + }, + { + "left_line": 201, + "right_line": 201 + }, + { + "left_line": 202, + "right_line": 202 + }, + { + "left_line": 203, + "right_line": 203 + }, + { + "left_line": 204, + "right_line": 204 + }, + { + "left_line": 205, + "right_line": 205 + }, + { + "left_line": 206, + "right_line": 206 + }, + { + "left_line": 207, + "right_line": 207 + }, + { + "left_line": 208, + "right_line": 208 + }, + { + "left_line": 209, + "right_line": 209 + }, + { + "left_line": 210, + "right_line": 210 + }, + { + "left_line": 211, + "right_line": 211 + }, + { + "left_line": 212, + "right_line": 212 + }, + { + "left_line": 213, + "right_line": 213 + }, + { + "left_line": 214, + "right_line": 214 + }, + { + "left_line": 215, + "right_line": 215 + }, + { + "left_line": 216, + "right_line": 216 + }, + { + "left_line": 217, + "right_line": 217 + }, + { + "left_line": 218, + "right_line": 218 + }, + { + "left_line": 219, + "right_line": 219 + }, + { + "left_line": 220, + "right_line": 220 + }, + { + "left_line": 221, + "right_line": 221 + }, + { + "left_line": 222, + "right_line": 222 + }, + { + "left_line": 223, + "right_line": 223 + }, + { + "left_line": 224, + "right_line": 224 + }, + { + "left_line": 225, + "right_line": 225 + }, + { + "left_line": 226, + "right_line": 226 + }, + { + "left_line": 227, + "right_line": 227 + }, + { + "left_line": 228, + "right_line": 228 + }, + { + "left_line": 229, + "right_line": 229 + }, + { + "left_line": 230, + "right_line": 230 + }, + { + "left_line": 231, + "right_line": 231 + }, + { + "left_line": 232, + "right_line": 232 + }, + { + "left_line": 233, + "right_line": 233 + }, + { + "left_line": 234, + "right_line": 234 + }, + { + "left_line": 235, + "right_line": 235 + }, + { + "left_line": 236, + "right_line": 236 + }, + { + "left_line": 237, + "right_line": 237 + }, + { + "left_line": 238, + "right_line": 238 + }, + { + "left_line": 239, + "right_line": 239 + }, + { + "left_line": 240, + "right_line": 240 + }, + { + "left_line": 241, + "right_line": 241 + }, + { + "left_line": 242, + "right_line": 242 + }, + { + "left_line": 243, + "right_line": 243 + }, + { + "left_line": 244, + "right_line": 244 + }, + { + "left_line": 245, + "right_line": 245 + }, + { + "left_line": 246, + "right_line": 246 + }, + { + "left_line": 247, + "right_line": 247 + }, + { + "left_line": 248, + "right_line": 248 + }, + { + "left_line": 249, + "right_line": 249 + }, + { + "left_line": 250, + "right_line": 250 + }, + { + "left_line": 251, + "right_line": 251 + }, + { + "left_line": 252, + "right_line": 252 + }, + { + "left_line": 253, + "right_line": 253 + }, + { + "left_line": 254, + "right_line": 254 + }, + { + "left_line": 255, + "right_line": 255 + }, + { + "left_line": 256, + "right_line": 256 + }, + { + "left_line": 257, + "right_line": 257 + }, + { + "left_line": 258, + "right_line": 258 + }, + { + "left_line": 259, + "right_line": 259 + }, + { + "left_line": 260, + "right_line": 260 + }, + { + "left_line": 261, + "right_line": 261 + }, + { + "left_line": 262, + "right_line": 262 + }, + { + "left_line": 263, + "right_line": 263 + }, + { + "left_line": 264, + "right_line": 264 + }, + { + "left_line": 265, + "right_line": 265 + }, + { + "left_line": 266, + "right_line": 266 + }, + { + "left_line": 267, + "right_line": 267 + }, + { + "left_line": 268, + "right_line": 268 + }, + { + "left_line": 269, + "right_line": 269 + }, + { + "left_line": 270, + "right_line": 270 + }, + { + "left_line": 271, + "right_line": 271 + }, + { + "left_line": 272, + "right_line": 272 + }, + { + "left_line": 273, + "right_line": 273 + }, + { + "left_line": 274, + "right_line": 274 + }, + { + "left_line": 275, + "right_line": 275 + }, + { + "left_line": 276, + "right_line": 276 + }, + { + "left_line": 277, + "right_line": 277 + }, + { + "left_line": 278, + "right_line": 278 + }, + { + "left_line": 279, + "right_line": 279 + }, + { + "left_line": 280, + "right_line": 280 + }, + { + "left_line": 281, + "right_line": 281 + }, + { + "left_line": 282, + "right_line": 282 + }, + { + "left_line": 283, + "right_line": 283 + }, + { + "left_line": 284, + "right_line": 284 + }, + { + "left_line": 285, + "right_line": 285 + }, + { + "left_line": 286, + "right_line": 286 + }, + { + "left_line": 287, + "right_line": 287 + }, + { + "left_line": 288, + "right_line": 288 + }, + { + "left_line": 289, + "right_line": 289 + }, + { + "left_line": 290, + "right_line": 290 + }, + { + "left_line": 291, + "right_line": 291 + }, + { + "left_line": 292, + "right_line": 292 + }, + { + "left_line": 293, + "right_line": 293 + }, + { + "left_line": 294, + "right_line": 294 + }, + { + "left_line": 295, + "right_line": 295 + }, + { + "left_line": 296, + "right_line": 296 + }, + { + "left_line": 297, + "right_line": 297 + }, + { + "left_line": 298, + "right_line": 298 + }, + { + "left_line": 299, + "right_line": 299 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/metadata.json b/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/metadata.json new file mode 100644 index 0000000..3f3301a --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "273629a76013942ece924369c82b4cc76648b58c", + "commit": "167f55579feaeac81bd1d06a924c9e76290bf799", + "message": "Fix warning about Hash construction. (#2028)\n\nFixes .", + "author": "Samuel Williams", + "date": "2024-07-12T00:32:32+12:00", + "language": "ruby", + "files_changed": 1, + "insertions": 1, + "deletions": 1 +} diff --git a/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/new.rb b/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/new.rb new file mode 100644 index 0000000..1223bb5 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/new.rb @@ -0,0 +1,299 @@ +# frozen_string_literal: true +# +# We don't need the full test helper for this standalone class. +# +require 'minitest/autorun' unless defined?(Minitest) + +require_relative '../lib/sinatra/indifferent_hash' + +class TestIndifferentHashBasics < Minitest::Test + def test_flattened_constructor + hash = Sinatra::IndifferentHash[:a, 1, ?b, 2] + assert_equal 1, hash[?a] + assert_equal 2, hash[?b] + end + + def test_pairs_constructor + hash = Sinatra::IndifferentHash[[[:a, 1], [?b, 2]]] + assert_equal 1, hash[?a] + assert_equal 2, hash[?b] + end + + def test_default_block + hash = Sinatra::IndifferentHash.new { |h, k| h[k] = k.upcase } + assert_nil hash.default + assert_equal ?A, hash.default(:a) + end + + def test_default_object + hash = Sinatra::IndifferentHash.new({:a=>1, ?b=>2}) + assert_equal({ ?a=>1, ?b=>2 }, hash.default) + assert_equal({ ?a=>1, ?b=>2 }, hash[:a]) + end + + def test_default_assignment + hash = Sinatra::IndifferentHash.new + hash.default = { :a=>1, ?b=>2 } + assert_equal({ ?a=>1, ?b=>2 }, hash.default) + assert_equal({ ?a=>1, ?b=>2 }, hash[:a]) + end + + def test_assignment + hash = Sinatra::IndifferentHash.new + hash[:a] = :a + hash[?b] = :b + hash[3] = 3 + hash[:simple_nested] = { :a=>:a, ?b=>:b } + + assert_equal :a, hash[?a] + assert_equal :b, hash[?b] + assert_equal 3, hash[3] + assert_equal({ ?a=>:a, ?b=>:b }, hash['simple_nested']) + assert_nil hash[?d] + end + + def test_merge! + # merge! is already mostly tested by the different constructors, so we + # really just need to test the block form here + hash = Sinatra::IndifferentHash[:a=>'a', ?b=>'b', 3=>3] + hash.merge!(?a=>'A', :b=>'B', :d=>'D') do |key, oldval, newval| + "#{oldval}*#{key}*#{newval}" + end + + assert_equal({ ?a=>'a*a*A', ?b=>'b*b*B', 3=>3, ?d=>'D' }, hash) + end +end + +class TestIndifferentHash < Minitest::Test + def setup + @hash = Sinatra::IndifferentHash[:a=>:a, ?b=>:b, 3=>3, + :simple_nested=>{ :a=>:a, ?b=>:b }, + :nested=>{ :a=>[{ :a=>:a, ?b=>:b }, :c, 4], ?f=>:f, 7=>7 } + ] + end + + def test_hash_constructor + assert_equal :a, @hash[?a] + assert_equal :b, @hash[?b] + assert_equal 3, @hash[3] + assert_equal({ ?a=>:a, ?b=>:b }, @hash['nested'][?a][0]) + assert_equal :c, @hash['nested'][?a][1] + assert_equal 4, @hash['nested'][?a][2] + assert_equal :f, @hash['nested'][?f] + assert_equal 7, @hash['nested'][7] + assert_equal :a, @hash['simple_nested'][?a] + assert_equal :b, @hash['simple_nested'][?b] + assert_nil @hash[?d] + end + + def test_assoc + assert_nil @hash.assoc(:d) + assert_equal [?a, :a], @hash.assoc(:a) + assert_equal [?b, :b], @hash.assoc(:b) + end + + def test_rassoc + assert_nil @hash.rassoc(:d) + assert_equal [?a, :a], @hash.rassoc(:a) + assert_equal [?b, :b], @hash.rassoc(:b) + assert_equal ['simple_nested', { ?a=>:a, ?b=>:b }], @hash.rassoc(:a=>:a, ?b=>:b) + end + + def test_fetch + assert_raises(KeyError) { @hash.fetch(:d) } + assert_equal 1, @hash.fetch(:d, 1) + assert_equal 2, @hash.fetch(:d) { 2 } + assert_equal ?d, @hash.fetch(:d) { |k| k } + assert_equal :a, @hash.fetch(:a, 1) + assert_equal :a, @hash.fetch(:a) { 2 } + end + + def test_symbolic_retrieval + assert_equal :a, @hash[:a] + assert_equal :b, @hash[:b] + assert_equal({ ?a=>:a, ?b=>:b }, @hash[:nested][:a][0]) + assert_equal :c, @hash[:nested][:a][1] + assert_equal 4, @hash[:nested][:a][2] + assert_equal :f, @hash[:nested][:f] + assert_equal 7, @hash[:nested][7] + assert_equal :a, @hash[:simple_nested][:a] + assert_equal :b, @hash[:simple_nested][:b] + assert_nil @hash[:d] + end + + def test_key + assert_nil @hash.key(:d) + assert_equal ?a, @hash.key(:a) + assert_equal 'simple_nested', @hash.key(:a=>:a, ?b=>:b) + end + + def test_key? + assert_operator @hash, :key?, :a + assert_operator @hash, :key?, ?b + assert_operator @hash, :key?, 3 + refute_operator @hash, :key?, :d + end + + def test_value? + assert_operator @hash, :value?, :a + assert_operator @hash, :value?, :b + assert_operator @hash, :value?, 3 + assert_operator @hash, :value?, { :a=>:a, ?b=>:b } + refute_operator @hash, :value?, :d + end + + def test_delete + @hash.delete(:a) + @hash.delete(?b) + assert_nil @hash[:a] + assert_nil @hash[?b] + end + + def test_dig + assert_equal :a, @hash.dig(:a) + assert_equal :b, @hash.dig(?b) + assert_nil @hash.dig(:d) + + assert_equal :a, @hash.dig(:simple_nested, :a) + assert_equal :b, @hash.dig('simple_nested', ?b) + assert_nil @hash.dig('simple_nested', :d) + + assert_equal :a, @hash.dig(:nested, :a, 0, :a) + assert_equal :b, @hash.dig('nested', ?a, 0, ?b) + assert_nil @hash.dig('nested', ?a, 0, :d) + end + + def test_slice + assert_equal Sinatra::IndifferentHash[a: :a], @hash.slice(:a) + assert_equal Sinatra::IndifferentHash[b: :b], @hash.slice(?b) + assert_equal Sinatra::IndifferentHash[3 => 3], @hash.slice(3) + assert_equal Sinatra::IndifferentHash.new, @hash.slice(:d) + assert_equal Sinatra::IndifferentHash[a: :a, b: :b, 3 => 3], @hash.slice(:a, :b, 3) + assert_equal Sinatra::IndifferentHash[simple_nested: { a: :a, ?b => :b }], @hash.slice(:simple_nested) + assert_equal Sinatra::IndifferentHash[nested: { a: [{ a: :a, ?b => :b }, :c, 4], ?f => :f, 7 => 7 }], @hash.slice(:nested) + end + + def test_fetch_values + assert_raises(KeyError) { @hash.fetch_values(3, :d) } + assert_equal [:a, :b, 3, ?D], @hash.fetch_values(:a, ?b, 3, :d) { |k| k.upcase } + end + + def test_values_at + assert_equal [:a, :b, 3, nil], @hash.values_at(:a, ?b, 3, :d) + end + + def test_merge + # merge just calls merge!, which is already thoroughly tested + hash2 = @hash.merge(?a=>1, :q=>2) { |key, oldval, newval| "#{oldval}*#{key}*#{newval}" } + + refute_equal @hash, hash2 + assert_equal 'a*a*1', hash2[:a] + assert_equal 2, hash2[?q] + end + + def test_merge_with_multiple_argument + hash = Sinatra::IndifferentHash.new.merge({a: 1}, {b: 2}, {c: 3}) + assert_equal 1, hash[?a] + assert_equal 2, hash[?b] + assert_equal 3, hash[?c] + + hash2 = Sinatra::IndifferentHash[d: 4] + hash3 = {e: 5} + hash.merge!(hash2, hash3) + + assert_equal 4, hash[?d] + assert_equal 5, hash[?e] + end + + def test_replace + @hash.replace(?a=>1, :q=>2) + assert_equal({ ?a=>1, ?q=>2 }, @hash) + end + + def test_transform_values! + @hash.transform_values! { |v| v.is_a?(Hash) ? Hash[v.to_a] : v } + + assert_instance_of Sinatra::IndifferentHash, @hash[:simple_nested] + end + + def test_transform_values + hash2 = @hash.transform_values { |v| v.respond_to?(:upcase) ? v.upcase : v } + + refute_equal @hash, hash2 + assert_equal :A, hash2[:a] + assert_equal :A, hash2[?a] + end + + def test_transform_keys! + @hash.transform_keys! { |k| k.respond_to?(:to_sym) ? k.to_sym : k } + + assert_equal :a, @hash[:a] + assert_equal :a, @hash[?a] + end + + def test_transform_keys + hash2 = @hash.transform_keys { |k| k.respond_to?(:upcase) ? k.upcase : k } + + refute_equal @hash, hash2 + refute_operator hash2, :key?, :a + refute_operator hash2, :key?, ?a + assert_equal :a, hash2[:A] + assert_equal :a, hash2[?A] + end + + def test_select + hash = @hash.select { |k, v| v == :a } + assert_equal Sinatra::IndifferentHash[a: :a], hash + assert_instance_of Sinatra::IndifferentHash, hash + + hash2 = @hash.select { |k, v| true } + assert_equal @hash, hash2 + assert_instance_of Sinatra::IndifferentHash, hash2 + + enum = @hash.select + assert_instance_of Enumerator, enum + end + + def test_select! + @hash.select! { |k, v| v == :a } + assert_equal Sinatra::IndifferentHash[a: :a], @hash + end + + def test_reject + hash = @hash.reject { |k, v| v != :a } + assert_equal Sinatra::IndifferentHash[a: :a], hash + assert_instance_of Sinatra::IndifferentHash, hash + + hash2 = @hash.reject { |k, v| false } + assert_equal @hash, hash2 + assert_instance_of Sinatra::IndifferentHash, hash2 + + enum = @hash.reject + assert_instance_of Enumerator, enum + end + + def test_reject! + @hash.reject! { |k, v| v != :a } + assert_equal Sinatra::IndifferentHash[a: :a], @hash + end + + def test_compact + hash_with_nil_values = @hash.merge({?z => nil}) + compacted_hash = hash_with_nil_values.compact + assert_equal @hash, compacted_hash + assert_instance_of Sinatra::IndifferentHash, compacted_hash + + empty_hash = Sinatra::IndifferentHash.new + compacted_hash = empty_hash.compact + assert_equal empty_hash, compacted_hash + + non_empty_hash = Sinatra::IndifferentHash[a: :a] + compacted_hash = non_empty_hash.compact + assert_equal non_empty_hash, compacted_hash + end + + def test_except + hash = @hash.except(?b, 3, :simple_nested, 'nested') + assert_equal Sinatra::IndifferentHash[a: :a], hash + end if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0") +end diff --git a/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/old.rb b/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/old.rb new file mode 100644 index 0000000..2105c18 --- /dev/null +++ b/tests/testdata/ruby_sinatra_fix_warning_about_hash_constru_8/old.rb @@ -0,0 +1,299 @@ +# frozen_string_literal: true +# +# We don't need the full test helper for this standalone class. +# +require 'minitest/autorun' unless defined?(Minitest) + +require_relative '../lib/sinatra/indifferent_hash' + +class TestIndifferentHashBasics < Minitest::Test + def test_flattened_constructor + hash = Sinatra::IndifferentHash[:a, 1, ?b, 2] + assert_equal 1, hash[?a] + assert_equal 2, hash[?b] + end + + def test_pairs_constructor + hash = Sinatra::IndifferentHash[[[:a, 1], [?b, 2]]] + assert_equal 1, hash[?a] + assert_equal 2, hash[?b] + end + + def test_default_block + hash = Sinatra::IndifferentHash.new { |h, k| h[k] = k.upcase } + assert_nil hash.default + assert_equal ?A, hash.default(:a) + end + + def test_default_object + hash = Sinatra::IndifferentHash.new(:a=>1, ?b=>2) + assert_equal({ ?a=>1, ?b=>2 }, hash.default) + assert_equal({ ?a=>1, ?b=>2 }, hash[:a]) + end + + def test_default_assignment + hash = Sinatra::IndifferentHash.new + hash.default = { :a=>1, ?b=>2 } + assert_equal({ ?a=>1, ?b=>2 }, hash.default) + assert_equal({ ?a=>1, ?b=>2 }, hash[:a]) + end + + def test_assignment + hash = Sinatra::IndifferentHash.new + hash[:a] = :a + hash[?b] = :b + hash[3] = 3 + hash[:simple_nested] = { :a=>:a, ?b=>:b } + + assert_equal :a, hash[?a] + assert_equal :b, hash[?b] + assert_equal 3, hash[3] + assert_equal({ ?a=>:a, ?b=>:b }, hash['simple_nested']) + assert_nil hash[?d] + end + + def test_merge! + # merge! is already mostly tested by the different constructors, so we + # really just need to test the block form here + hash = Sinatra::IndifferentHash[:a=>'a', ?b=>'b', 3=>3] + hash.merge!(?a=>'A', :b=>'B', :d=>'D') do |key, oldval, newval| + "#{oldval}*#{key}*#{newval}" + end + + assert_equal({ ?a=>'a*a*A', ?b=>'b*b*B', 3=>3, ?d=>'D' }, hash) + end +end + +class TestIndifferentHash < Minitest::Test + def setup + @hash = Sinatra::IndifferentHash[:a=>:a, ?b=>:b, 3=>3, + :simple_nested=>{ :a=>:a, ?b=>:b }, + :nested=>{ :a=>[{ :a=>:a, ?b=>:b }, :c, 4], ?f=>:f, 7=>7 } + ] + end + + def test_hash_constructor + assert_equal :a, @hash[?a] + assert_equal :b, @hash[?b] + assert_equal 3, @hash[3] + assert_equal({ ?a=>:a, ?b=>:b }, @hash['nested'][?a][0]) + assert_equal :c, @hash['nested'][?a][1] + assert_equal 4, @hash['nested'][?a][2] + assert_equal :f, @hash['nested'][?f] + assert_equal 7, @hash['nested'][7] + assert_equal :a, @hash['simple_nested'][?a] + assert_equal :b, @hash['simple_nested'][?b] + assert_nil @hash[?d] + end + + def test_assoc + assert_nil @hash.assoc(:d) + assert_equal [?a, :a], @hash.assoc(:a) + assert_equal [?b, :b], @hash.assoc(:b) + end + + def test_rassoc + assert_nil @hash.rassoc(:d) + assert_equal [?a, :a], @hash.rassoc(:a) + assert_equal [?b, :b], @hash.rassoc(:b) + assert_equal ['simple_nested', { ?a=>:a, ?b=>:b }], @hash.rassoc(:a=>:a, ?b=>:b) + end + + def test_fetch + assert_raises(KeyError) { @hash.fetch(:d) } + assert_equal 1, @hash.fetch(:d, 1) + assert_equal 2, @hash.fetch(:d) { 2 } + assert_equal ?d, @hash.fetch(:d) { |k| k } + assert_equal :a, @hash.fetch(:a, 1) + assert_equal :a, @hash.fetch(:a) { 2 } + end + + def test_symbolic_retrieval + assert_equal :a, @hash[:a] + assert_equal :b, @hash[:b] + assert_equal({ ?a=>:a, ?b=>:b }, @hash[:nested][:a][0]) + assert_equal :c, @hash[:nested][:a][1] + assert_equal 4, @hash[:nested][:a][2] + assert_equal :f, @hash[:nested][:f] + assert_equal 7, @hash[:nested][7] + assert_equal :a, @hash[:simple_nested][:a] + assert_equal :b, @hash[:simple_nested][:b] + assert_nil @hash[:d] + end + + def test_key + assert_nil @hash.key(:d) + assert_equal ?a, @hash.key(:a) + assert_equal 'simple_nested', @hash.key(:a=>:a, ?b=>:b) + end + + def test_key? + assert_operator @hash, :key?, :a + assert_operator @hash, :key?, ?b + assert_operator @hash, :key?, 3 + refute_operator @hash, :key?, :d + end + + def test_value? + assert_operator @hash, :value?, :a + assert_operator @hash, :value?, :b + assert_operator @hash, :value?, 3 + assert_operator @hash, :value?, { :a=>:a, ?b=>:b } + refute_operator @hash, :value?, :d + end + + def test_delete + @hash.delete(:a) + @hash.delete(?b) + assert_nil @hash[:a] + assert_nil @hash[?b] + end + + def test_dig + assert_equal :a, @hash.dig(:a) + assert_equal :b, @hash.dig(?b) + assert_nil @hash.dig(:d) + + assert_equal :a, @hash.dig(:simple_nested, :a) + assert_equal :b, @hash.dig('simple_nested', ?b) + assert_nil @hash.dig('simple_nested', :d) + + assert_equal :a, @hash.dig(:nested, :a, 0, :a) + assert_equal :b, @hash.dig('nested', ?a, 0, ?b) + assert_nil @hash.dig('nested', ?a, 0, :d) + end + + def test_slice + assert_equal Sinatra::IndifferentHash[a: :a], @hash.slice(:a) + assert_equal Sinatra::IndifferentHash[b: :b], @hash.slice(?b) + assert_equal Sinatra::IndifferentHash[3 => 3], @hash.slice(3) + assert_equal Sinatra::IndifferentHash.new, @hash.slice(:d) + assert_equal Sinatra::IndifferentHash[a: :a, b: :b, 3 => 3], @hash.slice(:a, :b, 3) + assert_equal Sinatra::IndifferentHash[simple_nested: { a: :a, ?b => :b }], @hash.slice(:simple_nested) + assert_equal Sinatra::IndifferentHash[nested: { a: [{ a: :a, ?b => :b }, :c, 4], ?f => :f, 7 => 7 }], @hash.slice(:nested) + end + + def test_fetch_values + assert_raises(KeyError) { @hash.fetch_values(3, :d) } + assert_equal [:a, :b, 3, ?D], @hash.fetch_values(:a, ?b, 3, :d) { |k| k.upcase } + end + + def test_values_at + assert_equal [:a, :b, 3, nil], @hash.values_at(:a, ?b, 3, :d) + end + + def test_merge + # merge just calls merge!, which is already thoroughly tested + hash2 = @hash.merge(?a=>1, :q=>2) { |key, oldval, newval| "#{oldval}*#{key}*#{newval}" } + + refute_equal @hash, hash2 + assert_equal 'a*a*1', hash2[:a] + assert_equal 2, hash2[?q] + end + + def test_merge_with_multiple_argument + hash = Sinatra::IndifferentHash.new.merge({a: 1}, {b: 2}, {c: 3}) + assert_equal 1, hash[?a] + assert_equal 2, hash[?b] + assert_equal 3, hash[?c] + + hash2 = Sinatra::IndifferentHash[d: 4] + hash3 = {e: 5} + hash.merge!(hash2, hash3) + + assert_equal 4, hash[?d] + assert_equal 5, hash[?e] + end + + def test_replace + @hash.replace(?a=>1, :q=>2) + assert_equal({ ?a=>1, ?q=>2 }, @hash) + end + + def test_transform_values! + @hash.transform_values! { |v| v.is_a?(Hash) ? Hash[v.to_a] : v } + + assert_instance_of Sinatra::IndifferentHash, @hash[:simple_nested] + end + + def test_transform_values + hash2 = @hash.transform_values { |v| v.respond_to?(:upcase) ? v.upcase : v } + + refute_equal @hash, hash2 + assert_equal :A, hash2[:a] + assert_equal :A, hash2[?a] + end + + def test_transform_keys! + @hash.transform_keys! { |k| k.respond_to?(:to_sym) ? k.to_sym : k } + + assert_equal :a, @hash[:a] + assert_equal :a, @hash[?a] + end + + def test_transform_keys + hash2 = @hash.transform_keys { |k| k.respond_to?(:upcase) ? k.upcase : k } + + refute_equal @hash, hash2 + refute_operator hash2, :key?, :a + refute_operator hash2, :key?, ?a + assert_equal :a, hash2[:A] + assert_equal :a, hash2[?A] + end + + def test_select + hash = @hash.select { |k, v| v == :a } + assert_equal Sinatra::IndifferentHash[a: :a], hash + assert_instance_of Sinatra::IndifferentHash, hash + + hash2 = @hash.select { |k, v| true } + assert_equal @hash, hash2 + assert_instance_of Sinatra::IndifferentHash, hash2 + + enum = @hash.select + assert_instance_of Enumerator, enum + end + + def test_select! + @hash.select! { |k, v| v == :a } + assert_equal Sinatra::IndifferentHash[a: :a], @hash + end + + def test_reject + hash = @hash.reject { |k, v| v != :a } + assert_equal Sinatra::IndifferentHash[a: :a], hash + assert_instance_of Sinatra::IndifferentHash, hash + + hash2 = @hash.reject { |k, v| false } + assert_equal @hash, hash2 + assert_instance_of Sinatra::IndifferentHash, hash2 + + enum = @hash.reject + assert_instance_of Enumerator, enum + end + + def test_reject! + @hash.reject! { |k, v| v != :a } + assert_equal Sinatra::IndifferentHash[a: :a], @hash + end + + def test_compact + hash_with_nil_values = @hash.merge({?z => nil}) + compacted_hash = hash_with_nil_values.compact + assert_equal @hash, compacted_hash + assert_instance_of Sinatra::IndifferentHash, compacted_hash + + empty_hash = Sinatra::IndifferentHash.new + compacted_hash = empty_hash.compact + assert_equal empty_hash, compacted_hash + + non_empty_hash = Sinatra::IndifferentHash[a: :a] + compacted_hash = non_empty_hash.compact + assert_equal non_empty_hash, compacted_hash + end + + def test_except + hash = @hash.except(?b, 3, :simple_nested, 'nested') + assert_equal Sinatra::IndifferentHash[a: :a], hash + end if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.0") +end diff --git a/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/expected.json b/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/expected.json new file mode 100644 index 0000000..088d596 --- /dev/null +++ b/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/expected.json @@ -0,0 +1,20276 @@ +{ + "version": "v1", + "actions": [ + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 0 + ], + "type": "comment", + "label": "# frozen_string_literal: true", + "start_byte": 0, + "end_byte": 29 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 1 + ], + "type": "comment", + "label": "# external dependencies", + "start_byte": 31, + "end_byte": 54 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 2 + ], + "type": "call", + "start_byte": 55, + "end_byte": 69 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 3 + ], + "type": "call", + "start_byte": 70, + "end_byte": 84 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 3, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 4 + ], + "type": "call", + "start_byte": 85, + "end_byte": 110 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 4, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 5 + ], + "type": "call", + "start_byte": 111, + "end_byte": 131 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 5, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 6 + ], + "type": "call", + "start_byte": 132, + "end_byte": 160 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 6, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 7 + ], + "type": "call", + "start_byte": 161, + "end_byte": 189 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 7, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 8 + ], + "type": "comment", + "label": "# stdlib dependencies", + "start_byte": 191, + "end_byte": 212 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 8 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 9 + ], + "type": "call", + "start_byte": 213, + "end_byte": 227 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 9, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 10 + ], + "type": "call", + "start_byte": 228, + "end_byte": 241 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 10, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 11 + ], + "type": "comment", + "label": "# other files we need", + "start_byte": 243, + "end_byte": 264 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 11 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 12 + ], + "type": "call", + "start_byte": 265, + "end_byte": 299 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 12, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 13 + ], + "type": "call", + "start_byte": 300, + "end_byte": 333 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 13, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 14 + ], + "type": "call", + "start_byte": 334, + "end_byte": 359 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 14, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15 + ], + "type": "module", + "start_byte": 361, + "end_byte": 66988 + }, + "parent": { + "tree": "after", + "path": null, + "type": "program", + "start_byte": 0, + "end_byte": 66989 + }, + "position": 15 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 1 + ], + "type": "constant", + "label": "Sinatra", + "start_byte": 368, + "end_byte": 375 + }, + "parent": { + "tree": "after", + "path": [ + 15 + ], + "type": "module", + "start_byte": 361, + "end_byte": 66988 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 2 + ], + "type": "comment", + "label": "# The request object. See Rack::Request for more info:", + "start_byte": 378, + "end_byte": 432 + }, + "parent": { + "tree": "after", + "path": [ + 15 + ], + "type": "module", + "start_byte": 361, + "end_byte": 66988 + }, + "position": 2 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 3 + ], + "type": "comment", + "label": "# https://rubydoc.info/github/rack/rack/main/Rack/Request", + "start_byte": 435, + "end_byte": 492 + }, + "parent": { + "tree": "after", + "path": [ + 15 + ], + "type": "module", + "start_byte": 361, + "end_byte": 66988 + }, + "position": 3 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 5 + ], + "type": "end", + "label": "end", + "start_byte": 66985, + "end_byte": 66988 + }, + "parent": { + "tree": "after", + "path": [ + 15 + ], + "type": "module", + "start_byte": 361, + "end_byte": 66988 + }, + "position": 5 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 0 + ], + "type": "class", + "start_byte": 495, + "end_byte": 4040 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 0, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 1 + ], + "type": "comment", + "label": "# The response object. See Rack::Response and Rack::Response::Helpers for", + "start_byte": 4044, + "end_byte": 4117 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 2 + ], + "type": "comment", + "label": "# more info:", + "start_byte": 4120, + "end_byte": 4132 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 2 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 3 + ], + "type": "comment", + "label": "# https://rubydoc.info/github/rack/rack/main/Rack/Response", + "start_byte": 4135, + "end_byte": 4193 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 3 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 4 + ], + "type": "comment", + "label": "# https://rubydoc.info/github/rack/rack/main/Rack/Response/Helpers", + "start_byte": 4196, + "end_byte": 4262 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 4 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 5 + ], + "type": "class", + "start_byte": 4265, + "end_byte": 5353 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 5, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 6 + ], + "type": "comment", + "label": "# Some Rack handlers (Rainbows!) implement an extended body object protocol, however,", + "start_byte": 5357, + "end_byte": 5442 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 6 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 7 + ], + "type": "comment", + "label": "# some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.", + "start_byte": 5445, + "end_byte": 5538 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 7 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 8 + ], + "type": "comment", + "label": "# This middleware will detect an extended body object and will make sure it reaches the", + "start_byte": 5541, + "end_byte": 5628 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 8 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 9 + ], + "type": "comment", + "label": "# handler directly. We do this here, so our middleware and middleware set up by the app will", + "start_byte": 5631, + "end_byte": 5723 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 9 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 10 + ], + "type": "comment", + "label": "# still be able to run.", + "start_byte": 5726, + "end_byte": 5749 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 10 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 11 + ], + "type": "class", + "start_byte": 5752, + "end_byte": 6618 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 11, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 12 + ], + "type": "comment", + "label": "# Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,", + "start_byte": 6622, + "end_byte": 6712 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 12 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 13 + ], + "type": "comment", + "label": "# if another CommonLogger is already in the middleware chain.", + "start_byte": 6715, + "end_byte": 6776 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 13 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 14 + ], + "type": "class", + "start_byte": 6779, + "end_byte": 7141 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 14, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 15 + ], + "type": "class", + "start_byte": 7145, + "end_byte": 7188 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 15, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 16 + ], + "type": "class", + "start_byte": 7192, + "end_byte": 7261 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 16, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 17 + ], + "type": "class", + "start_byte": 7265, + "end_byte": 7332 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 17, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 18 + ], + "type": "comment", + "label": "# Methods available to routes, before/after filters, and views.", + "start_byte": 7336, + "end_byte": 7399 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 18 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 19 + ], + "type": "module", + "start_byte": 7402, + "end_byte": 21094 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 19, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 20 + ], + "type": "comment", + "label": "# Template rendering methods. Each method takes the name of a template", + "start_byte": 21098, + "end_byte": 21168 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 20 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 21 + ], + "type": "comment", + "label": "# to render as a Symbol and returns a String with the rendered output,", + "start_byte": 21171, + "end_byte": 21241 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 21 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 22 + ], + "type": "comment", + "label": "# as well as an optional hash with additional options.", + "start_byte": 21244, + "end_byte": 21298 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 22 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 23 + ], + "type": "comment", + "label": "#", + "start_byte": 21301, + "end_byte": 21302 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 23 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 24 + ], + "type": "comment", + "label": "# `template` is either the name or path of the template as symbol", + "start_byte": 21305, + "end_byte": 21370 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 24 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 25 + ], + "type": "comment", + "label": "# (Use `:'subdir/myview'` for views in subdirectories), or a string", + "start_byte": 21373, + "end_byte": 21440 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 25 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 26 + ], + "type": "comment", + "label": "# that will be rendered.", + "start_byte": 21443, + "end_byte": 21467 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 26 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 27 + ], + "type": "comment", + "label": "#", + "start_byte": 21470, + "end_byte": 21471 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 27 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 28 + ], + "type": "comment", + "label": "# Possible options are:", + "start_byte": 21474, + "end_byte": 21497 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 28 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 29 + ], + "type": "comment", + "label": "# :content_type The content type to use, same arguments as content_type.", + "start_byte": 21500, + "end_byte": 21576 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 29 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 30 + ], + "type": "comment", + "label": "# :layout If set to something falsy, no layout is rendered, otherwise", + "start_byte": 21579, + "end_byte": 21658 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 30 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 31 + ], + "type": "comment", + "label": "# the specified layout is used (Ignored for `sass`)", + "start_byte": 21661, + "end_byte": 21730 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 31 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 32 + ], + "type": "comment", + "label": "# :layout_engine Engine to use for rendering the layout.", + "start_byte": 21733, + "end_byte": 21792 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 32 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 33 + ], + "type": "comment", + "label": "# :locals A hash with local variables that should be available", + "start_byte": 21795, + "end_byte": 21867 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 33 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 34 + ], + "type": "comment", + "label": "# in the template", + "start_byte": 21870, + "end_byte": 21905 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 34 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 35 + ], + "type": "comment", + "label": "# :scope If set, template is evaluate with the binding of the given", + "start_byte": 21908, + "end_byte": 21986 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 35 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 36 + ], + "type": "comment", + "label": "# object rather than the application instance.", + "start_byte": 21989, + "end_byte": 22053 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 36 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 37 + ], + "type": "comment", + "label": "# :views Views directory to use.", + "start_byte": 22056, + "end_byte": 22099 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 37 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 38 + ], + "type": "module", + "start_byte": 22102, + "end_byte": 28885 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 38, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 39 + ], + "type": "comment", + "label": "# Extremely simple template cache implementation.", + "start_byte": 28889, + "end_byte": 28938 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 39 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 40 + ], + "type": "comment", + "label": "# * Not thread-safe.", + "start_byte": 28941, + "end_byte": 28963 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 40 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 41 + ], + "type": "comment", + "label": "# * Size is unbounded.", + "start_byte": 28966, + "end_byte": 28990 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 41 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 42 + ], + "type": "comment", + "label": "# * Keys are not copied defensively, and should not be modified after", + "start_byte": 28993, + "end_byte": 29064 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 42 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 43 + ], + "type": "comment", + "label": "# being passed to #fetch. More specifically, the values returned by", + "start_byte": 29067, + "end_byte": 29139 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 43 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 44 + ], + "type": "comment", + "label": "# key#hash and key#eql? should not change.", + "start_byte": 29142, + "end_byte": 29188 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 44 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 45 + ], + "type": "comment", + "label": "#", + "start_byte": 29191, + "end_byte": 29192 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 45 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 46 + ], + "type": "comment", + "label": "# Implementation copied from Tilt::Cache.", + "start_byte": 29195, + "end_byte": 29236 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 46 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 47 + ], + "type": "class", + "start_byte": 29239, + "end_byte": 29727 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 47, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 48 + ], + "type": "comment", + "label": "# Base class for all Sinatra applications and middleware.", + "start_byte": 29731, + "end_byte": 29788 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 48 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 50 + ], + "type": "comment", + "label": "# Execution context for classic style (top-level) applications. All", + "start_byte": 64187, + "end_byte": 64254 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 50 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 51 + ], + "type": "comment", + "label": "# DSL methods executed on main are delegated to this class.", + "start_byte": 64257, + "end_byte": 64316 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 51 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 52 + ], + "type": "comment", + "label": "#", + "start_byte": 64319, + "end_byte": 64320 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 52 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 53 + ], + "type": "comment", + "label": "# The Application class should not be subclassed, unless you want to", + "start_byte": 64323, + "end_byte": 64391 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 53 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 54 + ], + "type": "comment", + "label": "# inherit all settings, routes, handlers, and error pages from the", + "start_byte": 64394, + "end_byte": 64460 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 54 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 55 + ], + "type": "comment", + "label": "# top-level. Subclassing Sinatra::Base is highly recommended for", + "start_byte": 64463, + "end_byte": 64527 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 55 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 56 + ], + "type": "comment", + "label": "# modular applications.", + "start_byte": 64530, + "end_byte": 64553 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 56 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 57 + ], + "type": "class", + "start_byte": 64556, + "end_byte": 64909 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 57, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 58 + ], + "type": "comment", + "label": "# Sinatra delegation mixin. Mixing this module into an object causes all", + "start_byte": 64913, + "end_byte": 64985 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 58 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 59 + ], + "type": "comment", + "label": "# methods to be delegated to the Sinatra::Application class. Used primarily", + "start_byte": 64988, + "end_byte": 65063 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 59 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 60 + ], + "type": "comment", + "label": "# at the top-level.", + "start_byte": 65066, + "end_byte": 65085 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 60 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 61 + ], + "type": "module", + "start_byte": 65088, + "end_byte": 65982 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 61, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 62 + ], + "type": "class", + "start_byte": 65986, + "end_byte": 66332 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 62, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 63 + ], + "type": "comment", + "label": "# Create a new Sinatra application; the block is evaluated in the class scope.", + "start_byte": 66336, + "end_byte": 66414 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 63 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 64 + ], + "type": "singleton_method", + "start_byte": 66417, + "end_byte": 66536 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 64, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 65 + ], + "type": "comment", + "label": "# Extend the top-level DSL with the modules provided.", + "start_byte": 66540, + "end_byte": 66593 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 65 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 66 + ], + "type": "singleton_method", + "start_byte": 66596, + "end_byte": 66691 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 66, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 67 + ], + "type": "comment", + "label": "# Include the helper modules provided in Sinatra's request context.", + "start_byte": 66695, + "end_byte": 66762 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 67 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 68 + ], + "type": "singleton_method", + "start_byte": 66765, + "end_byte": 66858 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 68, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 69 + ], + "type": "comment", + "label": "# Use the middleware for classic applications.", + "start_byte": 66862, + "end_byte": 66908 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 69 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 70 + ], + "type": "singleton_method", + "start_byte": 66911, + "end_byte": 66984 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4 + ], + "type": "body_statement", + "start_byte": 495, + "end_byte": 66984 + }, + "position": 70, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 1 + ], + "type": "constant", + "label": "Base", + "start_byte": 29797, + "end_byte": 29801 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49 + ], + "type": "class", + "start_byte": 29791, + "end_byte": 64183 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 3 + ], + "type": "end", + "label": "end", + "start_byte": 64180, + "end_byte": 64183 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49 + ], + "type": "class", + "start_byte": 29791, + "end_byte": 64183 + }, + "position": 3 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 0 + ], + "type": "call", + "start_byte": 29806, + "end_byte": 29825 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 0, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 1 + ], + "type": "call", + "start_byte": 29830, + "end_byte": 29845 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 2 + ], + "type": "call", + "start_byte": 29850, + "end_byte": 29867 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 3 + ], + "type": "assignment", + "start_byte": 29873, + "end_byte": 29903 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 3, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 4 + ], + "type": "call", + "start_byte": 29909, + "end_byte": 29963 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 4, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 5 + ], + "type": "call", + "start_byte": 29968, + "end_byte": 29997 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 5, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 6 + ], + "type": "method", + "start_byte": 30003, + "end_byte": 30233 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 6, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 7 + ], + "type": "comment", + "label": "# Rack call interface.", + "start_byte": 30239, + "end_byte": 30261 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 7 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 8 + ], + "type": "method", + "start_byte": 30266, + "end_byte": 30308 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 8, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 9 + ], + "type": "method", + "start_byte": 30314, + "end_byte": 30942 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 9, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 10 + ], + "type": "comment", + "label": "# Access settings defined with Base.set.", + "start_byte": 30948, + "end_byte": 30988 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 10 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 11 + ], + "type": "singleton_method", + "start_byte": 30993, + "end_byte": 31029 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 11, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 12 + ], + "type": "comment", + "label": "# Access settings defined with Base.set.", + "start_byte": 31035, + "end_byte": 31075 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 12 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 13 + ], + "type": "method", + "start_byte": 31080, + "end_byte": 31126 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 13, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 14 + ], + "type": "comment", + "label": "# Exit the current block, halts any further processing", + "start_byte": 31132, + "end_byte": 31186 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 14 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 15 + ], + "type": "comment", + "label": "# of the request, and returns the specified response.", + "start_byte": 31191, + "end_byte": 31244 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 15 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 16 + ], + "type": "method", + "start_byte": 31249, + "end_byte": 31360 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 16, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 17 + ], + "type": "comment", + "label": "# Pass control to the next matching route.", + "start_byte": 31366, + "end_byte": 31408 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 17 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 18 + ], + "type": "comment", + "label": "# If there are no more matching routes, Sinatra will", + "start_byte": 31413, + "end_byte": 31465 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 18 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 19 + ], + "type": "comment", + "label": "# return a 404 response.", + "start_byte": 31470, + "end_byte": 31494 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 19 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 20 + ], + "type": "method", + "start_byte": 31499, + "end_byte": 31548 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 20, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 21 + ], + "type": "comment", + "label": "# Forward the request to the downstream app -- middleware only.", + "start_byte": 31554, + "end_byte": 31617 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 21 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 22 + ], + "type": "method", + "start_byte": 31622, + "end_byte": 31862 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 22, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 23 + ], + "type": "identifier", + "label": "private", + "start_byte": 31868, + "end_byte": 31875 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 23 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 24 + ], + "type": "comment", + "label": "# Run filters defined on the class and all superclasses.", + "start_byte": 31881, + "end_byte": 31937 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 24 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 25 + ], + "type": "comment", + "label": "# Accepts an optional block to call after each filter is applied.", + "start_byte": 31942, + "end_byte": 32007 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 25 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 26 + ], + "type": "method", + "start_byte": 32012, + "end_byte": 32279 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 26, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 27 + ], + "type": "comment", + "label": "# Run routes defined on the class and all superclasses.", + "start_byte": 32285, + "end_byte": 32340 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 27 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 28 + ], + "type": "method", + "start_byte": 32345, + "end_byte": 33113 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 28, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 29 + ], + "type": "comment", + "label": "# Run a route block and throw :halt with the result.", + "start_byte": 33119, + "end_byte": 33171 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 29 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 30 + ], + "type": "method", + "start_byte": 33176, + "end_byte": 33223 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 30, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 31 + ], + "type": "comment", + "label": "# If the current request matches pattern and conditions, fill params", + "start_byte": 33229, + "end_byte": 33297 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 31 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 32 + ], + "type": "comment", + "label": "# with keys and call the given block.", + "start_byte": 33302, + "end_byte": 33339 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 32 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 33 + ], + "type": "comment", + "label": "# Revert params afterwards.", + "start_byte": 33344, + "end_byte": 33371 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 33 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 34 + ], + "type": "comment", + "label": "#", + "start_byte": 33376, + "end_byte": 33377 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 34 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 35 + ], + "type": "comment", + "label": "# Returns pass block.", + "start_byte": 33382, + "end_byte": 33403 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 35 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 36 + ], + "type": "method", + "start_byte": 33408, + "end_byte": 34811 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 36, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 37 + ], + "type": "comment", + "label": "# No matching route was found or all routes passed. The default", + "start_byte": 34817, + "end_byte": 34880 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 37 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 38 + ], + "type": "comment", + "label": "# implementation is to forward the request downstream when running", + "start_byte": 34885, + "end_byte": 34951 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 38 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 39 + ], + "type": "comment", + "label": "# as middleware (@app is non-nil); when no downstream app is set, raise", + "start_byte": 34956, + "end_byte": 35027 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 39 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 40 + ], + "type": "comment", + "label": "# a NotFound exception. Subclasses can override this method to perform", + "start_byte": 35032, + "end_byte": 35102 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 40 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 41 + ], + "type": "comment", + "label": "# custom route miss logic.", + "start_byte": 35107, + "end_byte": 35133 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 41 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 42 + ], + "type": "method", + "start_byte": 35138, + "end_byte": 35211 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 42, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 43 + ], + "type": "comment", + "label": "# Attempt to serve static files from public directory. Throws :halt when", + "start_byte": 35217, + "end_byte": 35289 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 43 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 44 + ], + "type": "comment", + "label": "# a matching file is found, returns nil otherwise.", + "start_byte": 35294, + "end_byte": 35344 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 44 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 45 + ], + "type": "method", + "start_byte": 35349, + "end_byte": 35881 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 45, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 46 + ], + "type": "comment", + "label": "# Run the block with 'throw :halt' support and apply result to the response.", + "start_byte": 35887, + "end_byte": 35963 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 46 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 47 + ], + "type": "method", + "start_byte": 35968, + "end_byte": 36355 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 47, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 48 + ], + "type": "comment", + "label": "# Dispatch a request with error handling.", + "start_byte": 36361, + "end_byte": 36402 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 48 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 49 + ], + "type": "method", + "start_byte": 36407, + "end_byte": 37150 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 49, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 50 + ], + "type": "comment", + "label": "# Error handling during requests.", + "start_byte": 37156, + "end_byte": 37189 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 50 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 51 + ], + "type": "method", + "start_byte": 37194, + "end_byte": 38576 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 51, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 52 + ], + "type": "comment", + "label": "# Find an custom error block for the key(s) specified.", + "start_byte": 38582, + "end_byte": 38636 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 52 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 53 + ], + "type": "method", + "start_byte": 38641, + "end_byte": 39204 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 53, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 54 + ], + "type": "method", + "start_byte": 39210, + "end_byte": 39779 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 54, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55 + ], + "type": "singleton_class", + "start_byte": 39785, + "end_byte": 59442 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 55 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 56 + ], + "type": "comment", + "label": "# Force data to specified encoding. It defaults to settings.default_encoding", + "start_byte": 59448, + "end_byte": 59524 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 56 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 57 + ], + "type": "comment", + "label": "# which is UTF-8 by default", + "start_byte": 59529, + "end_byte": 59556 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 57 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 58 + ], + "type": "singleton_method", + "start_byte": 59561, + "end_byte": 59984 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 58, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 59 + ], + "type": "method", + "start_byte": 59990, + "end_byte": 60060 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 59, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 60 + ], + "type": "call", + "start_byte": 60066, + "end_byte": 60072 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 60, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 61 + ], + "type": "call", + "start_byte": 60078, + "end_byte": 60154 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 61, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 62 + ], + "type": "call", + "start_byte": 60159, + "end_byte": 60192 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 62, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 63 + ], + "type": "call", + "start_byte": 60197, + "end_byte": 60230 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 63, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 64 + ], + "type": "call", + "start_byte": 60235, + "end_byte": 60278 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 64, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 65 + ], + "type": "call", + "start_byte": 60283, + "end_byte": 60303 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 65, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 66 + ], + "type": "call", + "start_byte": 60308, + "end_byte": 60361 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 66, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 67 + ], + "type": "call", + "start_byte": 60366, + "end_byte": 60385 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 67, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 68 + ], + "type": "call", + "start_byte": 60390, + "end_byte": 60411 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 68, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 69 + ], + "type": "call", + "start_byte": 60416, + "end_byte": 60443 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 69, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 70 + ], + "type": "call", + "start_byte": 60448, + "end_byte": 60468 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 70, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 71 + ], + "type": "call", + "start_byte": 60473, + "end_byte": 60503 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 71, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 72 + ], + "type": "call", + "start_byte": 60508, + "end_byte": 60528 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 72, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 73 + ], + "type": "call", + "start_byte": 60533, + "end_byte": 60610 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 73, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 74 + ], + "type": "binary", + "start_byte": 60615, + "end_byte": 60649 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 74, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 75 + ], + "type": "call", + "start_byte": 60654, + "end_byte": 60678 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 75, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 76 + ], + "type": "call", + "start_byte": 60683, + "end_byte": 60721 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 76, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 77 + ], + "type": "comment", + "label": "# explicitly generating a session secret eagerly to play nice with preforking", + "start_byte": 60727, + "end_byte": 60804 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 77 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 78 + ], + "type": "begin", + "start_byte": 60809, + "end_byte": 61207 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 78, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 79 + ], + "type": "singleton_class", + "start_byte": 61213, + "end_byte": 61324 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 79, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 80 + ], + "type": "call", + "start_byte": 61330, + "end_byte": 61345 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 80, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 81 + ], + "type": "comment", + "label": "# start server via at-exit hook?", + "start_byte": 61368, + "end_byte": 61400 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 81 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 82 + ], + "type": "call", + "start_byte": 61405, + "end_byte": 61429 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 82, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 83 + ], + "type": "call", + "start_byte": 61434, + "end_byte": 61456 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 83, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 84 + ], + "type": "call", + "start_byte": 61461, + "end_byte": 61477 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 84, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 85 + ], + "type": "call", + "start_byte": 61482, + "end_byte": 61511 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 85, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 86 + ], + "type": "call", + "start_byte": 61516, + "end_byte": 61574 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 86, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 87 + ], + "type": "call", + "start_byte": 61579, + "end_byte": 61654 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 87, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 88 + ], + "type": "call", + "start_byte": 61659, + "end_byte": 61676 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 88, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 89 + ], + "type": "assignment", + "start_byte": 61682, + "end_byte": 61732 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 89, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 90 + ], + "type": "if_modifier", + "start_byte": 61738, + "end_byte": 61789 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 90, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 91 + ], + "type": "if_modifier", + "start_byte": 61794, + "end_byte": 61845 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 91, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 92 + ], + "type": "if_modifier", + "start_byte": 61850, + "end_byte": 61901 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 92, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 93 + ], + "type": "call", + "start_byte": 61906, + "end_byte": 61927 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 93, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 94 + ], + "type": "call", + "start_byte": 61933, + "end_byte": 61962 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 94, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 95 + ], + "type": "call", + "start_byte": 61967, + "end_byte": 61997 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 95, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 96 + ], + "type": "call", + "start_byte": 62002, + "end_byte": 62027 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 96, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 97 + ], + "type": "call", + "start_byte": 62032, + "end_byte": 62055 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 97, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 98 + ], + "type": "call", + "start_byte": 62061, + "end_byte": 62079 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 98, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 99 + ], + "type": "call", + "start_byte": 62084, + "end_byte": 62156 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 99, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 100 + ], + "type": "call", + "start_byte": 62161, + "end_byte": 62214 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 100, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 101 + ], + "type": "call", + "start_byte": 62219, + "end_byte": 62263 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 101, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 102 + ], + "type": "call", + "start_byte": 62268, + "end_byte": 62284 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 102, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 103 + ], + "type": "call", + "start_byte": 62289, + "end_byte": 62308 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 103, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 104 + ], + "type": "call", + "start_byte": 62314, + "end_byte": 62376 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 104, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 105 + ], + "type": "call", + "start_byte": 62381, + "end_byte": 62446 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 105, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 106 + ], + "type": "call", + "start_byte": 62451, + "end_byte": 62483 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 106, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 107 + ], + "type": "call", + "start_byte": 62489, + "end_byte": 62615 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 107, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 108 + ], + "type": "call", + "start_byte": 62621, + "end_byte": 64177 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2 + ], + "type": "body_statement", + "start_byte": 29806, + "end_byte": 64177 + }, + "position": 108, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 1 + ], + "type": "bitwise_operator_literal", + "label": "\u003c\u003c", + "start_byte": 39791, + "end_byte": 39793 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55 + ], + "type": "singleton_class", + "start_byte": 39785, + "end_byte": 59442 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 2 + ], + "type": "self", + "label": "self", + "start_byte": 39794, + "end_byte": 39798 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55 + ], + "type": "singleton_class", + "start_byte": 39785, + "end_byte": 59442 + }, + "position": 2 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 4 + ], + "type": "end", + "label": "end", + "start_byte": 59439, + "end_byte": 59442 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55 + ], + "type": "singleton_class", + "start_byte": 39785, + "end_byte": 59442 + }, + "position": 4 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 0 + ], + "type": "assignment", + "start_byte": 39805, + "end_byte": 40527 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 0, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 1 + ], + "type": "call", + "start_byte": 40535, + "end_byte": 40624 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 2 + ], + "type": "method", + "start_byte": 40632, + "end_byte": 40689 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 3 + ], + "type": "comment", + "label": "# Removes all routes, filters, middleware and extension hooks from the", + "start_byte": 40697, + "end_byte": 40767 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 3 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 4 + ], + "type": "comment", + "label": "# current class (not routes/filters/... defined by its superclass).", + "start_byte": 40774, + "end_byte": 40841 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 4 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 5 + ], + "type": "method", + "start_byte": 40848, + "end_byte": 41307 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 5, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 6 + ], + "type": "comment", + "label": "# Extension modules registered on this class and all superclasses.", + "start_byte": 41315, + "end_byte": 41381 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 6 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 7 + ], + "type": "method", + "start_byte": 41388, + "end_byte": 41559 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 7, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 8 + ], + "type": "comment", + "label": "# Middleware used in this class and all superclasses.", + "start_byte": 41567, + "end_byte": 41620 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 8 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 9 + ], + "type": "method", + "start_byte": 41627, + "end_byte": 41791 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 9, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 10 + ], + "type": "comment", + "label": "# Sets an option to the given value. If the value is a proc,", + "start_byte": 41799, + "end_byte": 41860 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 10 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 11 + ], + "type": "comment", + "label": "# the proc will be called every time the option is accessed.", + "start_byte": 41867, + "end_byte": 41927 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 11 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 13 + ], + "type": "comment", + "label": "# Same as calling `set :option, true` for each of the given options.", + "start_byte": 43027, + "end_byte": 43095 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 13 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 14 + ], + "type": "method", + "start_byte": 43102, + "end_byte": 43172 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 14, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 15 + ], + "type": "comment", + "label": "# Same as calling `set :option, false` for each of the given options.", + "start_byte": 43180, + "end_byte": 43249 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 15 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 16 + ], + "type": "method", + "start_byte": 43256, + "end_byte": 43328 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 16, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 17 + ], + "type": "comment", + "label": "# Define a custom error handler. Optionally takes either an Exception", + "start_byte": 43336, + "end_byte": 43405 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 17 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 18 + ], + "type": "comment", + "label": "# class, or an HTTP status code to specify which errors should be", + "start_byte": 43412, + "end_byte": 43477 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 18 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 19 + ], + "type": "comment", + "label": "# handled.", + "start_byte": 43484, + "end_byte": 43494 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 19 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 20 + ], + "type": "method", + "start_byte": 43501, + "end_byte": 43786 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 20, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 21 + ], + "type": "comment", + "label": "# Sugar for `error(404) { ... }`", + "start_byte": 43794, + "end_byte": 43826 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 21 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 22 + ], + "type": "method", + "start_byte": 43833, + "end_byte": 43891 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 22, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 23 + ], + "type": "comment", + "label": "# Define a named template. The block must return the template source.", + "start_byte": 43899, + "end_byte": 43968 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 23 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 24 + ], + "type": "method", + "start_byte": 43975, + "end_byte": 44114 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 24, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 25 + ], + "type": "comment", + "label": "# Define the layout template. The block must return the template source.", + "start_byte": 44122, + "end_byte": 44194 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 25 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 26 + ], + "type": "method", + "start_byte": 44201, + "end_byte": 44275 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 26, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 27 + ], + "type": "comment", + "label": "# Load embedded templates from the file; uses the caller's __FILE__", + "start_byte": 44283, + "end_byte": 44350 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 27 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 28 + ], + "type": "comment", + "label": "# when no file is specified.", + "start_byte": 44357, + "end_byte": 44385 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 28 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 29 + ], + "type": "method", + "start_byte": 44392, + "end_byte": 45363 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 29, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 30 + ], + "type": "comment", + "label": "# Lookup or register a mime type in Rack's mime registry.", + "start_byte": 45371, + "end_byte": 45428 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 30 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 31 + ], + "type": "method", + "start_byte": 45435, + "end_byte": 45727 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 31, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 32 + ], + "type": "comment", + "label": "# provides all mime types matching type, including deprecated types:", + "start_byte": 45735, + "end_byte": 45803 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 32 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 33 + ], + "type": "comment", + "label": "# mime_types :html # =\u003e ['text/html']", + "start_byte": 45810, + "end_byte": 45849 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 33 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 34 + ], + "type": "comment", + "label": "# mime_types :js # =\u003e ['application/javascript', 'text/javascript']", + "start_byte": 45856, + "end_byte": 45927 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 34 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 35 + ], + "type": "method", + "start_byte": 45934, + "end_byte": 46077 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 35, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 36 + ], + "type": "comment", + "label": "# Define a before filter; runs before all requests within the same", + "start_byte": 46085, + "end_byte": 46151 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 36 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 37 + ], + "type": "comment", + "label": "# context as route handlers and may access/modify the request and", + "start_byte": 46158, + "end_byte": 46223 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 37 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 38 + ], + "type": "comment", + "label": "# response.", + "start_byte": 46230, + "end_byte": 46241 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 38 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 39 + ], + "type": "method", + "start_byte": 46248, + "end_byte": 46353 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 39, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 40 + ], + "type": "comment", + "label": "# Define an after filter; runs after all requests within the same", + "start_byte": 46361, + "end_byte": 46426 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 40 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 41 + ], + "type": "comment", + "label": "# context as route handlers and may access/modify the request and", + "start_byte": 46433, + "end_byte": 46498 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 41 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 42 + ], + "type": "comment", + "label": "# response.", + "start_byte": 46505, + "end_byte": 46516 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 42 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 43 + ], + "type": "method", + "start_byte": 46523, + "end_byte": 46626 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 43, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 44 + ], + "type": "comment", + "label": "# add a filter", + "start_byte": 46634, + "end_byte": 46648 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 44 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 45 + ], + "type": "method", + "start_byte": 46655, + "end_byte": 46781 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 45, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 46 + ], + "type": "method", + "start_byte": 46789, + "end_byte": 46878 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 46, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 47 + ], + "type": "method", + "start_byte": 46886, + "end_byte": 46971 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 47, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 48 + ], + "type": "comment", + "label": "# Add a route condition. The route is considered non-matching when the", + "start_byte": 46979, + "end_byte": 47049 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 48 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 49 + ], + "type": "comment", + "label": "# block returns false.", + "start_byte": 47056, + "end_byte": 47078 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 49 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 50 + ], + "type": "method", + "start_byte": 47085, + "end_byte": 47213 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 50, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 51 + ], + "type": "method", + "start_byte": 47221, + "end_byte": 47419 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 51, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 52 + ], + "type": "method", + "start_byte": 47427, + "end_byte": 47494 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 52, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 53 + ], + "type": "method", + "start_byte": 47502, + "end_byte": 47548 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 53, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 54 + ], + "type": "comment", + "label": "# Defining a `GET` handler also automatically defines", + "start_byte": 47556, + "end_byte": 47609 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 54 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 55 + ], + "type": "comment", + "label": "# a `HEAD` handler.", + "start_byte": 47616, + "end_byte": 47635 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 55 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 56 + ], + "type": "method", + "start_byte": 47642, + "end_byte": 47838 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 56, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 57 + ], + "type": "method", + "start_byte": 47846, + "end_byte": 47922 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 57, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 58 + ], + "type": "method", + "start_byte": 47930, + "end_byte": 48006 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 58, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 59 + ], + "type": "method", + "start_byte": 48014, + "end_byte": 48090 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 59, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 60 + ], + "type": "method", + "start_byte": 48098, + "end_byte": 48174 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 60, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 61 + ], + "type": "method", + "start_byte": 48182, + "end_byte": 48258 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 61, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 62 + ], + "type": "method", + "start_byte": 48266, + "end_byte": 48342 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 62, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 63 + ], + "type": "method", + "start_byte": 48350, + "end_byte": 48426 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 63, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 64 + ], + "type": "method", + "start_byte": 48434, + "end_byte": 48510 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 64, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 65 + ], + "type": "comment", + "label": "# Makes the methods defined in the block and in the Modules given", + "start_byte": 48518, + "end_byte": 48583 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 65 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 66 + ], + "type": "comment", + "label": "# in `extensions` available to the handlers and templates", + "start_byte": 48590, + "end_byte": 48647 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 66 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 67 + ], + "type": "method", + "start_byte": 48654, + "end_byte": 48789 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 67, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 68 + ], + "type": "comment", + "label": "# Register an extension. Alternatively take a block from which an", + "start_byte": 48797, + "end_byte": 48862 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 68 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 69 + ], + "type": "comment", + "label": "# extension will be created and registered on the fly.", + "start_byte": 48869, + "end_byte": 48923 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 69 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 70 + ], + "type": "method", + "start_byte": 48930, + "end_byte": 49217 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 70, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 71 + ], + "type": "method", + "start_byte": 49225, + "end_byte": 49274 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 71, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 72 + ], + "type": "method", + "start_byte": 49281, + "end_byte": 49330 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 72, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 73 + ], + "type": "method", + "start_byte": 49337, + "end_byte": 49386 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 73, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 74 + ], + "type": "comment", + "label": "# Set configuration options for Sinatra and/or the app.", + "start_byte": 49394, + "end_byte": 49449 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 74 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 75 + ], + "type": "comment", + "label": "# Allows scoping of settings for certain environments.", + "start_byte": 49456, + "end_byte": 49510 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 75 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 76 + ], + "type": "method", + "start_byte": 49517, + "end_byte": 49618 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 76, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 77 + ], + "type": "comment", + "label": "# Use the specified Rack middleware", + "start_byte": 49626, + "end_byte": 49661 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 77 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 78 + ], + "type": "method", + "start_byte": 49668, + "end_byte": 49786 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 78, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 79 + ], + "type": "if_modifier", + "start_byte": 49793, + "end_byte": 49851 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 79, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 80 + ], + "type": "comment", + "label": "# Stop the self-hosted server if running.", + "start_byte": 49859, + "end_byte": 49900 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 80 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 81 + ], + "type": "method", + "start_byte": 49907, + "end_byte": 50326 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 81, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 82 + ], + "type": "alias", + "start_byte": 50334, + "end_byte": 50351 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 82, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 83 + ], + "type": "comment", + "label": "# Run the Sinatra app as a self-hosted server using", + "start_byte": 50359, + "end_byte": 50410 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 83 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 84 + ], + "type": "comment", + "label": "# Puma, Falcon, or WEBrick (in that order). If given a block, will call", + "start_byte": 50417, + "end_byte": 50488 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 84 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 85 + ], + "type": "comment", + "label": "# with the constructed handler once we have taken the stage.", + "start_byte": 50495, + "end_byte": 50555 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 85 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 86 + ], + "type": "method", + "start_byte": 50562, + "end_byte": 51156 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 86, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 87 + ], + "type": "alias", + "start_byte": 51164, + "end_byte": 51181 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 87, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 88 + ], + "type": "comment", + "label": "# Check whether the self-hosted server is running or not.", + "start_byte": 51189, + "end_byte": 51246 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 88 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 89 + ], + "type": "method", + "start_byte": 51253, + "end_byte": 51299 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 89, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 90 + ], + "type": "comment", + "label": "# The prototype instance used to process requests.", + "start_byte": 51307, + "end_byte": 51357 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 90 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 91 + ], + "type": "method", + "start_byte": 51364, + "end_byte": 51414 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 91, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 92 + ], + "type": "comment", + "label": "# Create a new instance without middleware in front of it.", + "start_byte": 51422, + "end_byte": 51480 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 92 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 93 + ], + "type": "unless_modifier", + "start_byte": 51487, + "end_byte": 51530 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 93, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 94 + ], + "type": "comment", + "label": "# Create a new instance of the class fronted by its middleware", + "start_byte": 51538, + "end_byte": 51600 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 94 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 95 + ], + "type": "comment", + "label": "# pipeline. The object is guaranteed to respond to #call but may not be", + "start_byte": 51607, + "end_byte": 51678 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 95 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 96 + ], + "type": "comment", + "label": "# an instance of the class new was called on.", + "start_byte": 51685, + "end_byte": 51730 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 96 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 97 + ], + "type": "method", + "start_byte": 51737, + "end_byte": 51862 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 97, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 98 + ], + "type": "if_modifier", + "start_byte": 51869, + "end_byte": 51926 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 98, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 99 + ], + "type": "comment", + "label": "# Creates a Rack::Builder instance with all the middleware set up and", + "start_byte": 51934, + "end_byte": 52003 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 99 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 100 + ], + "type": "comment", + "label": "# the given +app+ as end point.", + "start_byte": 52010, + "end_byte": 52041 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 100 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 101 + ], + "type": "method", + "start_byte": 52048, + "end_byte": 52222 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 101, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 102 + ], + "type": "method", + "start_byte": 52230, + "end_byte": 52297 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 102, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 103 + ], + "type": "comment", + "label": "# Like Kernel#caller but excluding certain magic entries and without", + "start_byte": 52305, + "end_byte": 52373 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 103 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 104 + ], + "type": "comment", + "label": "# line / method information; the resulting array contains filenames only.", + "start_byte": 52380, + "end_byte": 52453 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 104 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 105 + ], + "type": "method", + "start_byte": 52460, + "end_byte": 52520 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 105, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 106 + ], + "type": "identifier", + "label": "private", + "start_byte": 52528, + "end_byte": 52535 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 106 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 107 + ], + "type": "comment", + "label": "# Starts the server by running the Rack Handler.", + "start_byte": 52543, + "end_byte": 52591 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 107 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 108 + ], + "type": "method", + "start_byte": 52598, + "end_byte": 53395 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 108, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 109 + ], + "type": "method", + "start_byte": 53403, + "end_byte": 53475 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 109, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 110 + ], + "type": "method", + "start_byte": 53483, + "end_byte": 53777 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 110, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 111 + ], + "type": "comment", + "label": "# Dynamically defines a method on settings.", + "start_byte": 53785, + "end_byte": 53828 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 111 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 112 + ], + "type": "method", + "start_byte": 53835, + "end_byte": 54101 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 112, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 113 + ], + "type": "comment", + "label": "# Condition for matching host name. Parameter might be String or Regexp.", + "start_byte": 54109, + "end_byte": 54181 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 113 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 114 + ], + "type": "method", + "start_byte": 54188, + "end_byte": 54267 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 114, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 115 + ], + "type": "comment", + "label": "# Condition for matching user agent. Parameter should be Regexp.", + "start_byte": 54275, + "end_byte": 54339 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 115 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 116 + ], + "type": "comment", + "label": "# Will set params[:agent].", + "start_byte": 54346, + "end_byte": 54372 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 116 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 117 + ], + "type": "method", + "start_byte": 54379, + "end_byte": 54597 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 117, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 118 + ], + "type": "alias", + "start_byte": 54604, + "end_byte": 54626 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 118, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 119 + ], + "type": "comment", + "label": "# Condition for matching mimetypes. Accepts file extensions.", + "start_byte": 54634, + "end_byte": 54694 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 119 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 120 + ], + "type": "method", + "start_byte": 54701, + "end_byte": 55313 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 120, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 121 + ], + "type": "method", + "start_byte": 55321, + "end_byte": 55618 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 121, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 122 + ], + "type": "method", + "start_byte": 55626, + "end_byte": 55739 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 122, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 123 + ], + "type": "method", + "start_byte": 55747, + "end_byte": 55934 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 123, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 124 + ], + "type": "method", + "start_byte": 55942, + "end_byte": 56763 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 124, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 125 + ], + "type": "method", + "start_byte": 56771, + "end_byte": 56903 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 125, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 126 + ], + "type": "method", + "start_byte": 56911, + "end_byte": 57243 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 126, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 127 + ], + "type": "method", + "start_byte": 57251, + "end_byte": 57351 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 127, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 128 + ], + "type": "method", + "start_byte": 57359, + "end_byte": 57573 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 128, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 129 + ], + "type": "method", + "start_byte": 57581, + "end_byte": 57658 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 129, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 130 + ], + "type": "method", + "start_byte": 57666, + "end_byte": 57750 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 130, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 131 + ], + "type": "method", + "start_byte": 57758, + "end_byte": 57943 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 131, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 132 + ], + "type": "method", + "start_byte": 57951, + "end_byte": 58404 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 132, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 133 + ], + "type": "method", + "start_byte": 58412, + "end_byte": 58680 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 133, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 134 + ], + "type": "method", + "start_byte": 58688, + "end_byte": 58836 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 134, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 135 + ], + "type": "assignment", + "start_byte": 58844, + "end_byte": 58863 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 135, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 136 + ], + "type": "method", + "start_byte": 58870, + "end_byte": 58999 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 136, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 137 + ], + "type": "comment", + "label": "# used for deprecation warnings", + "start_byte": 59007, + "end_byte": 59038 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 137 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 138 + ], + "type": "method", + "start_byte": 59045, + "end_byte": 59156 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 138, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 139 + ], + "type": "comment", + "label": "# Like Kernel#caller but excluding certain magic entries", + "start_byte": 59164, + "end_byte": 59220 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 139 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 140 + ], + "type": "method", + "start_byte": 59227, + "end_byte": 59434 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3 + ], + "type": "body_statement", + "start_byte": 39805, + "end_byte": 59434 + }, + "position": 140, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 0 + ], + "type": "def", + "label": "def", + "start_byte": 41934, + "end_byte": 41937 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12 + ], + "type": "method", + "start_byte": 41934, + "end_byte": 43019 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 1 + ], + "type": "identifier", + "label": "set", + "start_byte": 41938, + "end_byte": 41941 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12 + ], + "type": "method", + "start_byte": 41934, + "end_byte": 43019 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 2 + ], + "type": "method_parameters", + "start_byte": 41941, + "end_byte": 42006 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12 + ], + "type": "method", + "start_byte": 41934, + "end_byte": 43019 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 4 + ], + "type": "end", + "label": "end", + "start_byte": 43016, + "end_byte": 43019 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12 + ], + "type": "method", + "start_byte": 41934, + "end_byte": 43019 + }, + "position": 4 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 0 + ], + "type": "if_modifier", + "start_byte": 42015, + "end_byte": 42055 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 0, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 1 + ], + "type": "if", + "start_byte": 42065, + "end_byte": 42135 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 2 + ], + "type": "if", + "start_byte": 42145, + "end_byte": 42296 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 3 + ], + "type": "if", + "start_byte": 42306, + "end_byte": 42411 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 3, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 4 + ], + "type": "assignment", + "start_byte": 42421, + "end_byte": 42466 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 4, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 5 + ], + "type": "assignment", + "start_byte": 42475, + "end_byte": 42498 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 5, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 7 + ], + "type": "call", + "start_byte": 42827, + "end_byte": 42865 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 7, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 8 + ], + "type": "call", + "start_byte": 42874, + "end_byte": 42906 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 8, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 9 + ], + "type": "unless_modifier", + "start_byte": 42915, + "end_byte": 42996 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 9, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 10 + ], + "type": "self", + "label": "self", + "start_byte": 43005, + "end_byte": 43009 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3 + ], + "type": "body_statement", + "start_byte": 42015, + "end_byte": 43009 + }, + "position": 10 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 1 + ], + "type": "identifier", + "label": "value", + "start_byte": 42513, + "end_byte": 42518 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6 + ], + "type": "case", + "start_byte": 42508, + "end_byte": 42817 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 2 + ], + "type": "when", + "start_byte": 42527, + "end_byte": 42561 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6 + ], + "type": "case", + "start_byte": 42508, + "end_byte": 42817 + }, + "position": 2, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 3 + ], + "type": "when", + "start_byte": 42570, + "end_byte": 42656 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6 + ], + "type": "case", + "start_byte": 42508, + "end_byte": 42817 + }, + "position": 3, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 5 + ], + "type": "end", + "label": "end", + "start_byte": 42814, + "end_byte": 42817 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6 + ], + "type": "case", + "start_byte": 42508, + "end_byte": 42817 + }, + "position": 5 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 1 + ], + "type": "pattern", + "start_byte": 42670, + "end_byte": 42674 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4 + ], + "type": "when", + "start_byte": 42665, + "end_byte": 42805 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2 + ], + "type": "then", + "start_byte": 42674, + "end_byte": 42805 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4 + ], + "type": "when", + "start_byte": 42665, + "end_byte": 42805 + }, + "position": 2 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 0 + ], + "type": "identifier", + "label": "setter", + "start_byte": 42685, + "end_byte": 42691 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0 + ], + "type": "assignment", + "start_byte": 42685, + "end_byte": 42805 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 1 + ], + "type": "assignment_operator_literal", + "label": "=", + "start_byte": 42692, + "end_byte": 42693 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0 + ], + "type": "assignment", + "start_byte": 42685, + "end_byte": 42805 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 0 + ], + "type": "identifier", + "label": "proc", + "start_byte": 42694, + "end_byte": 42698 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2 + ], + "type": "call", + "start_byte": 42694, + "end_byte": 42805 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 0 + ], + "type": "do", + "label": "do", + "start_byte": 42699, + "end_byte": 42701 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1 + ], + "type": "do_block", + "start_byte": 42699, + "end_byte": 42805 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 1 + ], + "type": "block_parameters", + "start_byte": 42702, + "end_byte": 42707 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1 + ], + "type": "do_block", + "start_byte": 42699, + "end_byte": 42805 + }, + "position": 1, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 3 + ], + "type": "end", + "label": "end", + "start_byte": 42802, + "end_byte": 42805 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1 + ], + "type": "do_block", + "start_byte": 42699, + "end_byte": 42805 + }, + "position": 3 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 0 + ], + "type": "if_modifier", + "start_byte": 42720, + "end_byte": 42757 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2 + ], + "type": "body_statement", + "start_byte": 42720, + "end_byte": 42791 + }, + "position": 0, + "subtree": true + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 1, + 0 + ], + "type": "identifier", + "label": "set", + "start_byte": 42770, + "end_byte": 42773 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 1 + ], + "type": "call", + "start_byte": 42770, + "end_byte": 42791 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 1, + 1, + 0 + ], + "type": "identifier", + "label": "option", + "start_byte": 42774, + "end_byte": 42780 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 1, + 1 + ], + "type": "argument_list", + "start_byte": 42774, + "end_byte": 42791 + }, + "position": 0 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 1, + 1, + 1 + ], + "type": "identifier", + "label": "val", + "start_byte": 42782, + "end_byte": 42785 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 1, + 1 + ], + "type": "argument_list", + "start_byte": 42774, + "end_byte": 42791 + }, + "position": 1 + }, + { + "action": "insert", + "node": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 1, + 1, + 2 + ], + "type": "true", + "label": "true", + "start_byte": 42787, + "end_byte": 42791 + }, + "parent": { + "tree": "after", + "path": [ + 15, + 4, + 49, + 2, + 55, + 3, + 12, + 3, + 6, + 4, + 2, + 0, + 2, + 1, + 2, + 1, + 1 + ], + "type": "argument_list", + "start_byte": 42774, + "end_byte": 42791 + }, + "position": 2 + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 0 + ], + "type": "constant", + "label": "Not", + "start_byte": 5, + "end_byte": 8 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 1, + 1, + 0 + ], + "type": "constant", + "label": "Found", + "start_byte": 9, + "end_byte": 14 + } + }, + { + "action": "delete", + "node": { + "tree": "before", + "path": [ + 0 + ], + "type": "integer", + "label": "404", + "start_byte": 0, + "end_byte": 3 + } + } + ], + "line_alignment": [ + { + "left_line": 0, + "right_line": 0 + }, + { + "left_line": -1, + "right_line": 1 + }, + { + "left_line": -1, + "right_line": 2 + }, + { + "left_line": -1, + "right_line": 3 + }, + { + "left_line": -1, + "right_line": 4 + }, + { + "left_line": -1, + "right_line": 5 + }, + { + "left_line": -1, + "right_line": 6 + }, + { + "left_line": -1, + "right_line": 7 + }, + { + "left_line": -1, + "right_line": 8 + }, + { + "left_line": -1, + "right_line": 9 + }, + { + "left_line": -1, + "right_line": 10 + }, + { + "left_line": -1, + "right_line": 11 + }, + { + "left_line": -1, + "right_line": 12 + }, + { + "left_line": -1, + "right_line": 13 + }, + { + "left_line": -1, + "right_line": 14 + }, + { + "left_line": -1, + "right_line": 15 + }, + { + "left_line": -1, + "right_line": 16 + }, + { + "left_line": -1, + "right_line": 17 + }, + { + "left_line": -1, + "right_line": 18 + }, + { + "left_line": -1, + "right_line": 19 + }, + { + "left_line": -1, + "right_line": 20 + }, + { + "left_line": -1, + "right_line": 21 + }, + { + "left_line": -1, + "right_line": 22 + }, + { + "left_line": -1, + "right_line": 23 + }, + { + "left_line": -1, + "right_line": 24 + }, + { + "left_line": -1, + "right_line": 25 + }, + { + "left_line": -1, + "right_line": 26 + }, + { + "left_line": -1, + "right_line": 27 + }, + { + "left_line": -1, + "right_line": 28 + }, + { + "left_line": -1, + "right_line": 29 + }, + { + "left_line": -1, + "right_line": 30 + }, + { + "left_line": -1, + "right_line": 31 + }, + { + "left_line": -1, + "right_line": 32 + }, + { + "left_line": -1, + "right_line": 33 + }, + { + "left_line": -1, + "right_line": 34 + }, + { + "left_line": -1, + "right_line": 35 + }, + { + "left_line": -1, + "right_line": 36 + }, + { + "left_line": -1, + "right_line": 37 + }, + { + "left_line": -1, + "right_line": 38 + }, + { + "left_line": -1, + "right_line": 39 + }, + { + "left_line": -1, + "right_line": 40 + }, + { + "left_line": -1, + "right_line": 41 + }, + { + "left_line": -1, + "right_line": 42 + }, + { + "left_line": -1, + "right_line": 43 + }, + { + "left_line": -1, + "right_line": 44 + }, + { + "left_line": -1, + "right_line": 45 + }, + { + "left_line": -1, + "right_line": 46 + }, + { + "left_line": -1, + "right_line": 47 + }, + { + "left_line": -1, + "right_line": 48 + }, + { + "left_line": -1, + "right_line": 49 + }, + { + "left_line": -1, + "right_line": 50 + }, + { + "left_line": -1, + "right_line": 51 + }, + { + "left_line": -1, + "right_line": 52 + }, + { + "left_line": -1, + "right_line": 53 + }, + { + "left_line": -1, + "right_line": 54 + }, + { + "left_line": -1, + "right_line": 55 + }, + { + "left_line": -1, + "right_line": 56 + }, + { + "left_line": -1, + "right_line": 57 + }, + { + "left_line": -1, + "right_line": 58 + }, + { + "left_line": -1, + "right_line": 59 + }, + { + "left_line": -1, + "right_line": 60 + }, + { + "left_line": -1, + "right_line": 61 + }, + { + "left_line": -1, + "right_line": 62 + }, + { + "left_line": -1, + "right_line": 63 + }, + { + "left_line": -1, + "right_line": 64 + }, + { + "left_line": -1, + "right_line": 65 + }, + { + "left_line": -1, + "right_line": 66 + }, + { + "left_line": -1, + "right_line": 67 + }, + { + "left_line": -1, + "right_line": 68 + }, + { + "left_line": -1, + "right_line": 69 + }, + { + "left_line": -1, + "right_line": 70 + }, + { + "left_line": -1, + "right_line": 71 + }, + { + "left_line": -1, + "right_line": 72 + }, + { + "left_line": -1, + "right_line": 73 + }, + { + "left_line": -1, + "right_line": 74 + }, + { + "left_line": -1, + "right_line": 75 + }, + { + "left_line": -1, + "right_line": 76 + }, + { + "left_line": -1, + "right_line": 77 + }, + { + "left_line": -1, + "right_line": 78 + }, + { + "left_line": -1, + "right_line": 79 + }, + { + "left_line": -1, + "right_line": 80 + }, + { + "left_line": -1, + "right_line": 81 + }, + { + "left_line": -1, + "right_line": 82 + }, + { + "left_line": -1, + "right_line": 83 + }, + { + "left_line": -1, + "right_line": 84 + }, + { + "left_line": -1, + "right_line": 85 + }, + { + "left_line": -1, + "right_line": 86 + }, + { + "left_line": -1, + "right_line": 87 + }, + { + "left_line": -1, + "right_line": 88 + }, + { + "left_line": -1, + "right_line": 89 + }, + { + "left_line": -1, + "right_line": 90 + }, + { + "left_line": -1, + "right_line": 91 + }, + { + "left_line": -1, + "right_line": 92 + }, + { + "left_line": -1, + "right_line": 93 + }, + { + "left_line": -1, + "right_line": 94 + }, + { + "left_line": -1, + "right_line": 95 + }, + { + "left_line": -1, + "right_line": 96 + }, + { + "left_line": -1, + "right_line": 97 + }, + { + "left_line": -1, + "right_line": 98 + }, + { + "left_line": -1, + "right_line": 99 + }, + { + "left_line": -1, + "right_line": 100 + }, + { + "left_line": -1, + "right_line": 101 + }, + { + "left_line": -1, + "right_line": 102 + }, + { + "left_line": -1, + "right_line": 103 + }, + { + "left_line": -1, + "right_line": 104 + }, + { + "left_line": -1, + "right_line": 105 + }, + { + "left_line": -1, + "right_line": 106 + }, + { + "left_line": -1, + "right_line": 107 + }, + { + "left_line": -1, + "right_line": 108 + }, + { + "left_line": -1, + "right_line": 109 + }, + { + "left_line": -1, + "right_line": 110 + }, + { + "left_line": -1, + "right_line": 111 + }, + { + "left_line": -1, + "right_line": 112 + }, + { + "left_line": -1, + "right_line": 113 + }, + { + "left_line": -1, + "right_line": 114 + }, + { + "left_line": -1, + "right_line": 115 + }, + { + "left_line": -1, + "right_line": 116 + }, + { + "left_line": -1, + "right_line": 117 + }, + { + "left_line": -1, + "right_line": 118 + }, + { + "left_line": -1, + "right_line": 119 + }, + { + "left_line": -1, + "right_line": 120 + }, + { + "left_line": -1, + "right_line": 121 + }, + { + "left_line": -1, + "right_line": 122 + }, + { + "left_line": -1, + "right_line": 123 + }, + { + "left_line": -1, + "right_line": 124 + }, + { + "left_line": -1, + "right_line": 125 + }, + { + "left_line": -1, + "right_line": 126 + }, + { + "left_line": -1, + "right_line": 127 + }, + { + "left_line": -1, + "right_line": 128 + }, + { + "left_line": -1, + "right_line": 129 + }, + { + "left_line": -1, + "right_line": 130 + }, + { + "left_line": -1, + "right_line": 131 + }, + { + "left_line": -1, + "right_line": 132 + }, + { + "left_line": -1, + "right_line": 133 + }, + { + "left_line": -1, + "right_line": 134 + }, + { + "left_line": -1, + "right_line": 135 + }, + { + "left_line": -1, + "right_line": 136 + }, + { + "left_line": -1, + "right_line": 137 + }, + { + "left_line": -1, + "right_line": 138 + }, + { + "left_line": -1, + "right_line": 139 + }, + { + "left_line": -1, + "right_line": 140 + }, + { + "left_line": -1, + "right_line": 141 + }, + { + "left_line": -1, + "right_line": 142 + }, + { + "left_line": -1, + "right_line": 143 + }, + { + "left_line": -1, + "right_line": 144 + }, + { + "left_line": -1, + "right_line": 145 + }, + { + "left_line": -1, + "right_line": 146 + }, + { + "left_line": -1, + "right_line": 147 + }, + { + "left_line": -1, + "right_line": 148 + }, + { + "left_line": -1, + "right_line": 149 + }, + { + "left_line": -1, + "right_line": 150 + }, + { + "left_line": -1, + "right_line": 151 + }, + { + "left_line": -1, + "right_line": 152 + }, + { + "left_line": -1, + "right_line": 153 + }, + { + "left_line": -1, + "right_line": 154 + }, + { + "left_line": -1, + "right_line": 155 + }, + { + "left_line": -1, + "right_line": 156 + }, + { + "left_line": -1, + "right_line": 157 + }, + { + "left_line": -1, + "right_line": 158 + }, + { + "left_line": -1, + "right_line": 159 + }, + { + "left_line": -1, + "right_line": 160 + }, + { + "left_line": -1, + "right_line": 161 + }, + { + "left_line": -1, + "right_line": 162 + }, + { + "left_line": -1, + "right_line": 163 + }, + { + "left_line": -1, + "right_line": 164 + }, + { + "left_line": -1, + "right_line": 165 + }, + { + "left_line": -1, + "right_line": 166 + }, + { + "left_line": -1, + "right_line": 167 + }, + { + "left_line": -1, + "right_line": 168 + }, + { + "left_line": -1, + "right_line": 169 + }, + { + "left_line": -1, + "right_line": 170 + }, + { + "left_line": -1, + "right_line": 171 + }, + { + "left_line": -1, + "right_line": 172 + }, + { + "left_line": -1, + "right_line": 173 + }, + { + "left_line": -1, + "right_line": 174 + }, + { + "left_line": -1, + "right_line": 175 + }, + { + "left_line": -1, + "right_line": 176 + }, + { + "left_line": -1, + "right_line": 177 + }, + { + "left_line": -1, + "right_line": 178 + }, + { + "left_line": -1, + "right_line": 179 + }, + { + "left_line": -1, + "right_line": 180 + }, + { + "left_line": -1, + "right_line": 181 + }, + { + "left_line": -1, + "right_line": 182 + }, + { + "left_line": -1, + "right_line": 183 + }, + { + "left_line": -1, + "right_line": 184 + }, + { + "left_line": -1, + "right_line": 185 + }, + { + "left_line": -1, + "right_line": 186 + }, + { + "left_line": -1, + "right_line": 187 + }, + { + "left_line": -1, + "right_line": 188 + }, + { + "left_line": -1, + "right_line": 189 + }, + { + "left_line": -1, + "right_line": 190 + }, + { + "left_line": -1, + "right_line": 191 + }, + { + "left_line": -1, + "right_line": 192 + }, + { + "left_line": -1, + "right_line": 193 + }, + { + "left_line": -1, + "right_line": 194 + }, + { + "left_line": -1, + "right_line": 195 + }, + { + "left_line": -1, + "right_line": 196 + }, + { + "left_line": -1, + "right_line": 197 + }, + { + "left_line": -1, + "right_line": 198 + }, + { + "left_line": -1, + "right_line": 199 + }, + { + "left_line": -1, + "right_line": 200 + }, + { + "left_line": -1, + "right_line": 201 + }, + { + "left_line": -1, + "right_line": 202 + }, + { + "left_line": -1, + "right_line": 203 + }, + { + "left_line": -1, + "right_line": 204 + }, + { + "left_line": -1, + "right_line": 205 + }, + { + "left_line": -1, + "right_line": 206 + }, + { + "left_line": -1, + "right_line": 207 + }, + { + "left_line": -1, + "right_line": 208 + }, + { + "left_line": -1, + "right_line": 209 + }, + { + "left_line": -1, + "right_line": 210 + }, + { + "left_line": -1, + "right_line": 211 + }, + { + "left_line": -1, + "right_line": 212 + }, + { + "left_line": -1, + "right_line": 213 + }, + { + "left_line": -1, + "right_line": 214 + }, + { + "left_line": -1, + "right_line": 215 + }, + { + "left_line": -1, + "right_line": 216 + }, + { + "left_line": -1, + "right_line": 217 + }, + { + "left_line": -1, + "right_line": 218 + }, + { + "left_line": -1, + "right_line": 219 + }, + { + "left_line": -1, + "right_line": 220 + }, + { + "left_line": -1, + "right_line": 221 + }, + { + "left_line": -1, + "right_line": 222 + }, + { + "left_line": -1, + "right_line": 223 + }, + { + "left_line": -1, + "right_line": 224 + }, + { + "left_line": -1, + "right_line": 225 + }, + { + "left_line": -1, + "right_line": 226 + }, + { + "left_line": -1, + "right_line": 227 + }, + { + "left_line": -1, + "right_line": 228 + }, + { + "left_line": -1, + "right_line": 229 + }, + { + "left_line": -1, + "right_line": 230 + }, + { + "left_line": -1, + "right_line": 231 + }, + { + "left_line": -1, + "right_line": 232 + }, + { + "left_line": -1, + "right_line": 233 + }, + { + "left_line": -1, + "right_line": 234 + }, + { + "left_line": -1, + "right_line": 235 + }, + { + "left_line": -1, + "right_line": 236 + }, + { + "left_line": -1, + "right_line": 237 + }, + { + "left_line": -1, + "right_line": 238 + }, + { + "left_line": -1, + "right_line": 239 + }, + { + "left_line": -1, + "right_line": 240 + }, + { + "left_line": -1, + "right_line": 241 + }, + { + "left_line": -1, + "right_line": 242 + }, + { + "left_line": -1, + "right_line": 243 + }, + { + "left_line": -1, + "right_line": 244 + }, + { + "left_line": -1, + "right_line": 245 + }, + { + "left_line": -1, + "right_line": 246 + }, + { + "left_line": -1, + "right_line": 247 + }, + { + "left_line": -1, + "right_line": 248 + }, + { + "left_line": -1, + "right_line": 249 + }, + { + "left_line": -1, + "right_line": 250 + }, + { + "left_line": -1, + "right_line": 251 + }, + { + "left_line": -1, + "right_line": 252 + }, + { + "left_line": -1, + "right_line": 253 + }, + { + "left_line": -1, + "right_line": 254 + }, + { + "left_line": -1, + "right_line": 255 + }, + { + "left_line": -1, + "right_line": 256 + }, + { + "left_line": -1, + "right_line": 257 + }, + { + "left_line": -1, + "right_line": 258 + }, + { + "left_line": -1, + "right_line": 259 + }, + { + "left_line": -1, + "right_line": 260 + }, + { + "left_line": -1, + "right_line": 261 + }, + { + "left_line": -1, + "right_line": 262 + }, + { + "left_line": -1, + "right_line": 263 + }, + { + "left_line": -1, + "right_line": 264 + }, + { + "left_line": -1, + "right_line": 265 + }, + { + "left_line": -1, + "right_line": 266 + }, + { + "left_line": -1, + "right_line": 267 + }, + { + "left_line": -1, + "right_line": 268 + }, + { + "left_line": -1, + "right_line": 269 + }, + { + "left_line": -1, + "right_line": 270 + }, + { + "left_line": -1, + "right_line": 271 + }, + { + "left_line": -1, + "right_line": 272 + }, + { + "left_line": -1, + "right_line": 273 + }, + { + "left_line": -1, + "right_line": 274 + }, + { + "left_line": -1, + "right_line": 275 + }, + { + "left_line": -1, + "right_line": 276 + }, + { + "left_line": -1, + "right_line": 277 + }, + { + "left_line": -1, + "right_line": 278 + }, + { + "left_line": -1, + "right_line": 279 + }, + { + "left_line": -1, + "right_line": 280 + }, + { + "left_line": -1, + "right_line": 281 + }, + { + "left_line": -1, + "right_line": 282 + }, + { + "left_line": -1, + "right_line": 283 + }, + { + "left_line": -1, + "right_line": 284 + }, + { + "left_line": -1, + "right_line": 285 + }, + { + "left_line": -1, + "right_line": 286 + }, + { + "left_line": -1, + "right_line": 287 + }, + { + "left_line": -1, + "right_line": 288 + }, + { + "left_line": -1, + "right_line": 289 + }, + { + "left_line": -1, + "right_line": 290 + }, + { + "left_line": -1, + "right_line": 291 + }, + { + "left_line": -1, + "right_line": 292 + }, + { + "left_line": -1, + "right_line": 293 + }, + { + "left_line": -1, + "right_line": 294 + }, + { + "left_line": -1, + "right_line": 295 + }, + { + "left_line": -1, + "right_line": 296 + }, + { + "left_line": -1, + "right_line": 297 + }, + { + "left_line": -1, + "right_line": 298 + }, + { + "left_line": -1, + "right_line": 299 + }, + { + "left_line": -1, + "right_line": 300 + }, + { + "left_line": -1, + "right_line": 301 + }, + { + "left_line": -1, + "right_line": 302 + }, + { + "left_line": -1, + "right_line": 303 + }, + { + "left_line": -1, + "right_line": 304 + }, + { + "left_line": -1, + "right_line": 305 + }, + { + "left_line": -1, + "right_line": 306 + }, + { + "left_line": -1, + "right_line": 307 + }, + { + "left_line": -1, + "right_line": 308 + }, + { + "left_line": -1, + "right_line": 309 + }, + { + "left_line": -1, + "right_line": 310 + }, + { + "left_line": -1, + "right_line": 311 + }, + { + "left_line": -1, + "right_line": 312 + }, + { + "left_line": -1, + "right_line": 313 + }, + { + "left_line": -1, + "right_line": 314 + }, + { + "left_line": -1, + "right_line": 315 + }, + { + "left_line": -1, + "right_line": 316 + }, + { + "left_line": -1, + "right_line": 317 + }, + { + "left_line": -1, + "right_line": 318 + }, + { + "left_line": -1, + "right_line": 319 + }, + { + "left_line": -1, + "right_line": 320 + }, + { + "left_line": -1, + "right_line": 321 + }, + { + "left_line": -1, + "right_line": 322 + }, + { + "left_line": -1, + "right_line": 323 + }, + { + "left_line": -1, + "right_line": 324 + }, + { + "left_line": -1, + "right_line": 325 + }, + { + "left_line": -1, + "right_line": 326 + }, + { + "left_line": -1, + "right_line": 327 + }, + { + "left_line": -1, + "right_line": 328 + }, + { + "left_line": -1, + "right_line": 329 + }, + { + "left_line": -1, + "right_line": 330 + }, + { + "left_line": -1, + "right_line": 331 + }, + { + "left_line": -1, + "right_line": 332 + }, + { + "left_line": -1, + "right_line": 333 + }, + { + "left_line": -1, + "right_line": 334 + }, + { + "left_line": -1, + "right_line": 335 + }, + { + "left_line": -1, + "right_line": 336 + }, + { + "left_line": -1, + "right_line": 337 + }, + { + "left_line": -1, + "right_line": 338 + }, + { + "left_line": -1, + "right_line": 339 + }, + { + "left_line": -1, + "right_line": 340 + }, + { + "left_line": -1, + "right_line": 341 + }, + { + "left_line": -1, + "right_line": 342 + }, + { + "left_line": -1, + "right_line": 343 + }, + { + "left_line": -1, + "right_line": 344 + }, + { + "left_line": -1, + "right_line": 345 + }, + { + "left_line": -1, + "right_line": 346 + }, + { + "left_line": -1, + "right_line": 347 + }, + { + "left_line": -1, + "right_line": 348 + }, + { + "left_line": -1, + "right_line": 349 + }, + { + "left_line": -1, + "right_line": 350 + }, + { + "left_line": -1, + "right_line": 351 + }, + { + "left_line": -1, + "right_line": 352 + }, + { + "left_line": -1, + "right_line": 353 + }, + { + "left_line": -1, + "right_line": 354 + }, + { + "left_line": -1, + "right_line": 355 + }, + { + "left_line": -1, + "right_line": 356 + }, + { + "left_line": -1, + "right_line": 357 + }, + { + "left_line": -1, + "right_line": 358 + }, + { + "left_line": -1, + "right_line": 359 + }, + { + "left_line": -1, + "right_line": 360 + }, + { + "left_line": -1, + "right_line": 361 + }, + { + "left_line": -1, + "right_line": 362 + }, + { + "left_line": -1, + "right_line": 363 + }, + { + "left_line": -1, + "right_line": 364 + }, + { + "left_line": -1, + "right_line": 365 + }, + { + "left_line": -1, + "right_line": 366 + }, + { + "left_line": -1, + "right_line": 367 + }, + { + "left_line": -1, + "right_line": 368 + }, + { + "left_line": -1, + "right_line": 369 + }, + { + "left_line": -1, + "right_line": 370 + }, + { + "left_line": -1, + "right_line": 371 + }, + { + "left_line": -1, + "right_line": 372 + }, + { + "left_line": -1, + "right_line": 373 + }, + { + "left_line": -1, + "right_line": 374 + }, + { + "left_line": -1, + "right_line": 375 + }, + { + "left_line": -1, + "right_line": 376 + }, + { + "left_line": -1, + "right_line": 377 + }, + { + "left_line": -1, + "right_line": 378 + }, + { + "left_line": -1, + "right_line": 379 + }, + { + "left_line": -1, + "right_line": 380 + }, + { + "left_line": -1, + "right_line": 381 + }, + { + "left_line": -1, + "right_line": 382 + }, + { + "left_line": -1, + "right_line": 383 + }, + { + "left_line": -1, + "right_line": 384 + }, + { + "left_line": -1, + "right_line": 385 + }, + { + "left_line": -1, + "right_line": 386 + }, + { + "left_line": -1, + "right_line": 387 + }, + { + "left_line": -1, + "right_line": 388 + }, + { + "left_line": -1, + "right_line": 389 + }, + { + "left_line": -1, + "right_line": 390 + }, + { + "left_line": -1, + "right_line": 391 + }, + { + "left_line": -1, + "right_line": 392 + }, + { + "left_line": -1, + "right_line": 393 + }, + { + "left_line": -1, + "right_line": 394 + }, + { + "left_line": -1, + "right_line": 395 + }, + { + "left_line": -1, + "right_line": 396 + }, + { + "left_line": -1, + "right_line": 397 + }, + { + "left_line": -1, + "right_line": 398 + }, + { + "left_line": -1, + "right_line": 399 + }, + { + "left_line": -1, + "right_line": 400 + }, + { + "left_line": -1, + "right_line": 401 + }, + { + "left_line": -1, + "right_line": 402 + }, + { + "left_line": -1, + "right_line": 403 + }, + { + "left_line": -1, + "right_line": 404 + }, + { + "left_line": -1, + "right_line": 405 + }, + { + "left_line": -1, + "right_line": 406 + }, + { + "left_line": -1, + "right_line": 407 + }, + { + "left_line": -1, + "right_line": 408 + }, + { + "left_line": -1, + "right_line": 409 + }, + { + "left_line": -1, + "right_line": 410 + }, + { + "left_line": -1, + "right_line": 411 + }, + { + "left_line": -1, + "right_line": 412 + }, + { + "left_line": -1, + "right_line": 413 + }, + { + "left_line": -1, + "right_line": 414 + }, + { + "left_line": -1, + "right_line": 415 + }, + { + "left_line": -1, + "right_line": 416 + }, + { + "left_line": -1, + "right_line": 417 + }, + { + "left_line": -1, + "right_line": 418 + }, + { + "left_line": -1, + "right_line": 419 + }, + { + "left_line": -1, + "right_line": 420 + }, + { + "left_line": -1, + "right_line": 421 + }, + { + "left_line": -1, + "right_line": 422 + }, + { + "left_line": -1, + "right_line": 423 + }, + { + "left_line": -1, + "right_line": 424 + }, + { + "left_line": -1, + "right_line": 425 + }, + { + "left_line": -1, + "right_line": 426 + }, + { + "left_line": -1, + "right_line": 427 + }, + { + "left_line": -1, + "right_line": 428 + }, + { + "left_line": -1, + "right_line": 429 + }, + { + "left_line": -1, + "right_line": 430 + }, + { + "left_line": -1, + "right_line": 431 + }, + { + "left_line": -1, + "right_line": 432 + }, + { + "left_line": -1, + "right_line": 433 + }, + { + "left_line": -1, + "right_line": 434 + }, + { + "left_line": -1, + "right_line": 435 + }, + { + "left_line": -1, + "right_line": 436 + }, + { + "left_line": -1, + "right_line": 437 + }, + { + "left_line": -1, + "right_line": 438 + }, + { + "left_line": -1, + "right_line": 439 + }, + { + "left_line": -1, + "right_line": 440 + }, + { + "left_line": -1, + "right_line": 441 + }, + { + "left_line": -1, + "right_line": 442 + }, + { + "left_line": -1, + "right_line": 443 + }, + { + "left_line": -1, + "right_line": 444 + }, + { + "left_line": -1, + "right_line": 445 + }, + { + "left_line": -1, + "right_line": 446 + }, + { + "left_line": -1, + "right_line": 447 + }, + { + "left_line": -1, + "right_line": 448 + }, + { + "left_line": -1, + "right_line": 449 + }, + { + "left_line": -1, + "right_line": 450 + }, + { + "left_line": -1, + "right_line": 451 + }, + { + "left_line": -1, + "right_line": 452 + }, + { + "left_line": -1, + "right_line": 453 + }, + { + "left_line": -1, + "right_line": 454 + }, + { + "left_line": -1, + "right_line": 455 + }, + { + "left_line": -1, + "right_line": 456 + }, + { + "left_line": -1, + "right_line": 457 + }, + { + "left_line": -1, + "right_line": 458 + }, + { + "left_line": -1, + "right_line": 459 + }, + { + "left_line": -1, + "right_line": 460 + }, + { + "left_line": -1, + "right_line": 461 + }, + { + "left_line": -1, + "right_line": 462 + }, + { + "left_line": -1, + "right_line": 463 + }, + { + "left_line": -1, + "right_line": 464 + }, + { + "left_line": -1, + "right_line": 465 + }, + { + "left_line": -1, + "right_line": 466 + }, + { + "left_line": -1, + "right_line": 467 + }, + { + "left_line": -1, + "right_line": 468 + }, + { + "left_line": -1, + "right_line": 469 + }, + { + "left_line": -1, + "right_line": 470 + }, + { + "left_line": -1, + "right_line": 471 + }, + { + "left_line": -1, + "right_line": 472 + }, + { + "left_line": -1, + "right_line": 473 + }, + { + "left_line": -1, + "right_line": 474 + }, + { + "left_line": -1, + "right_line": 475 + }, + { + "left_line": -1, + "right_line": 476 + }, + { + "left_line": -1, + "right_line": 477 + }, + { + "left_line": -1, + "right_line": 478 + }, + { + "left_line": -1, + "right_line": 479 + }, + { + "left_line": -1, + "right_line": 480 + }, + { + "left_line": -1, + "right_line": 481 + }, + { + "left_line": -1, + "right_line": 482 + }, + { + "left_line": -1, + "right_line": 483 + }, + { + "left_line": -1, + "right_line": 484 + }, + { + "left_line": -1, + "right_line": 485 + }, + { + "left_line": -1, + "right_line": 486 + }, + { + "left_line": -1, + "right_line": 487 + }, + { + "left_line": -1, + "right_line": 488 + }, + { + "left_line": -1, + "right_line": 489 + }, + { + "left_line": -1, + "right_line": 490 + }, + { + "left_line": -1, + "right_line": 491 + }, + { + "left_line": -1, + "right_line": 492 + }, + { + "left_line": -1, + "right_line": 493 + }, + { + "left_line": -1, + "right_line": 494 + }, + { + "left_line": -1, + "right_line": 495 + }, + { + "left_line": -1, + "right_line": 496 + }, + { + "left_line": -1, + "right_line": 497 + }, + { + "left_line": -1, + "right_line": 498 + }, + { + "left_line": -1, + "right_line": 499 + }, + { + "left_line": -1, + "right_line": 500 + }, + { + "left_line": -1, + "right_line": 501 + }, + { + "left_line": -1, + "right_line": 502 + }, + { + "left_line": -1, + "right_line": 503 + }, + { + "left_line": -1, + "right_line": 504 + }, + { + "left_line": -1, + "right_line": 505 + }, + { + "left_line": -1, + "right_line": 506 + }, + { + "left_line": -1, + "right_line": 507 + }, + { + "left_line": -1, + "right_line": 508 + }, + { + "left_line": -1, + "right_line": 509 + }, + { + "left_line": -1, + "right_line": 510 + }, + { + "left_line": -1, + "right_line": 511 + }, + { + "left_line": -1, + "right_line": 512 + }, + { + "left_line": -1, + "right_line": 513 + }, + { + "left_line": -1, + "right_line": 514 + }, + { + "left_line": -1, + "right_line": 515 + }, + { + "left_line": -1, + "right_line": 516 + }, + { + "left_line": -1, + "right_line": 517 + }, + { + "left_line": -1, + "right_line": 518 + }, + { + "left_line": -1, + "right_line": 519 + }, + { + "left_line": -1, + "right_line": 520 + }, + { + "left_line": -1, + "right_line": 521 + }, + { + "left_line": -1, + "right_line": 522 + }, + { + "left_line": -1, + "right_line": 523 + }, + { + "left_line": -1, + "right_line": 524 + }, + { + "left_line": -1, + "right_line": 525 + }, + { + "left_line": -1, + "right_line": 526 + }, + { + "left_line": -1, + "right_line": 527 + }, + { + "left_line": -1, + "right_line": 528 + }, + { + "left_line": -1, + "right_line": 529 + }, + { + "left_line": -1, + "right_line": 530 + }, + { + "left_line": -1, + "right_line": 531 + }, + { + "left_line": -1, + "right_line": 532 + }, + { + "left_line": -1, + "right_line": 533 + }, + { + "left_line": -1, + "right_line": 534 + }, + { + "left_line": -1, + "right_line": 535 + }, + { + "left_line": -1, + "right_line": 536 + }, + { + "left_line": -1, + "right_line": 537 + }, + { + "left_line": -1, + "right_line": 538 + }, + { + "left_line": -1, + "right_line": 539 + }, + { + "left_line": -1, + "right_line": 540 + }, + { + "left_line": -1, + "right_line": 541 + }, + { + "left_line": -1, + "right_line": 542 + }, + { + "left_line": -1, + "right_line": 543 + }, + { + "left_line": -1, + "right_line": 544 + }, + { + "left_line": -1, + "right_line": 545 + }, + { + "left_line": -1, + "right_line": 546 + }, + { + "left_line": -1, + "right_line": 547 + }, + { + "left_line": -1, + "right_line": 548 + }, + { + "left_line": -1, + "right_line": 549 + }, + { + "left_line": -1, + "right_line": 550 + }, + { + "left_line": -1, + "right_line": 551 + }, + { + "left_line": -1, + "right_line": 552 + }, + { + "left_line": -1, + "right_line": 553 + }, + { + "left_line": -1, + "right_line": 554 + }, + { + "left_line": -1, + "right_line": 555 + }, + { + "left_line": -1, + "right_line": 556 + }, + { + "left_line": -1, + "right_line": 557 + }, + { + "left_line": -1, + "right_line": 558 + }, + { + "left_line": -1, + "right_line": 559 + }, + { + "left_line": -1, + "right_line": 560 + }, + { + "left_line": -1, + "right_line": 561 + }, + { + "left_line": -1, + "right_line": 562 + }, + { + "left_line": -1, + "right_line": 563 + }, + { + "left_line": -1, + "right_line": 564 + }, + { + "left_line": -1, + "right_line": 565 + }, + { + "left_line": -1, + "right_line": 566 + }, + { + "left_line": -1, + "right_line": 567 + }, + { + "left_line": -1, + "right_line": 568 + }, + { + "left_line": -1, + "right_line": 569 + }, + { + "left_line": -1, + "right_line": 570 + }, + { + "left_line": -1, + "right_line": 571 + }, + { + "left_line": -1, + "right_line": 572 + }, + { + "left_line": -1, + "right_line": 573 + }, + { + "left_line": -1, + "right_line": 574 + }, + { + "left_line": -1, + "right_line": 575 + }, + { + "left_line": -1, + "right_line": 576 + }, + { + "left_line": -1, + "right_line": 577 + }, + { + "left_line": -1, + "right_line": 578 + }, + { + "left_line": -1, + "right_line": 579 + }, + { + "left_line": -1, + "right_line": 580 + }, + { + "left_line": -1, + "right_line": 581 + }, + { + "left_line": -1, + "right_line": 582 + }, + { + "left_line": -1, + "right_line": 583 + }, + { + "left_line": -1, + "right_line": 584 + }, + { + "left_line": -1, + "right_line": 585 + }, + { + "left_line": -1, + "right_line": 586 + }, + { + "left_line": -1, + "right_line": 587 + }, + { + "left_line": -1, + "right_line": 588 + }, + { + "left_line": -1, + "right_line": 589 + }, + { + "left_line": -1, + "right_line": 590 + }, + { + "left_line": -1, + "right_line": 591 + }, + { + "left_line": -1, + "right_line": 592 + }, + { + "left_line": -1, + "right_line": 593 + }, + { + "left_line": -1, + "right_line": 594 + }, + { + "left_line": -1, + "right_line": 595 + }, + { + "left_line": -1, + "right_line": 596 + }, + { + "left_line": -1, + "right_line": 597 + }, + { + "left_line": -1, + "right_line": 598 + }, + { + "left_line": -1, + "right_line": 599 + }, + { + "left_line": -1, + "right_line": 600 + }, + { + "left_line": -1, + "right_line": 601 + }, + { + "left_line": -1, + "right_line": 602 + }, + { + "left_line": -1, + "right_line": 603 + }, + { + "left_line": -1, + "right_line": 604 + }, + { + "left_line": -1, + "right_line": 605 + }, + { + "left_line": -1, + "right_line": 606 + }, + { + "left_line": -1, + "right_line": 607 + }, + { + "left_line": -1, + "right_line": 608 + }, + { + "left_line": -1, + "right_line": 609 + }, + { + "left_line": -1, + "right_line": 610 + }, + { + "left_line": -1, + "right_line": 611 + }, + { + "left_line": -1, + "right_line": 612 + }, + { + "left_line": -1, + "right_line": 613 + }, + { + "left_line": -1, + "right_line": 614 + }, + { + "left_line": -1, + "right_line": 615 + }, + { + "left_line": -1, + "right_line": 616 + }, + { + "left_line": -1, + "right_line": 617 + }, + { + "left_line": -1, + "right_line": 618 + }, + { + "left_line": -1, + "right_line": 619 + }, + { + "left_line": -1, + "right_line": 620 + }, + { + "left_line": -1, + "right_line": 621 + }, + { + "left_line": -1, + "right_line": 622 + }, + { + "left_line": -1, + "right_line": 623 + }, + { + "left_line": -1, + "right_line": 624 + }, + { + "left_line": -1, + "right_line": 625 + }, + { + "left_line": -1, + "right_line": 626 + }, + { + "left_line": -1, + "right_line": 627 + }, + { + "left_line": -1, + "right_line": 628 + }, + { + "left_line": -1, + "right_line": 629 + }, + { + "left_line": -1, + "right_line": 630 + }, + { + "left_line": -1, + "right_line": 631 + }, + { + "left_line": -1, + "right_line": 632 + }, + { + "left_line": -1, + "right_line": 633 + }, + { + "left_line": -1, + "right_line": 634 + }, + { + "left_line": -1, + "right_line": 635 + }, + { + "left_line": -1, + "right_line": 636 + }, + { + "left_line": -1, + "right_line": 637 + }, + { + "left_line": -1, + "right_line": 638 + }, + { + "left_line": -1, + "right_line": 639 + }, + { + "left_line": -1, + "right_line": 640 + }, + { + "left_line": -1, + "right_line": 641 + }, + { + "left_line": -1, + "right_line": 642 + }, + { + "left_line": -1, + "right_line": 643 + }, + { + "left_line": -1, + "right_line": 644 + }, + { + "left_line": -1, + "right_line": 645 + }, + { + "left_line": -1, + "right_line": 646 + }, + { + "left_line": -1, + "right_line": 647 + }, + { + "left_line": -1, + "right_line": 648 + }, + { + "left_line": -1, + "right_line": 649 + }, + { + "left_line": -1, + "right_line": 650 + }, + { + "left_line": -1, + "right_line": 651 + }, + { + "left_line": -1, + "right_line": 652 + }, + { + "left_line": -1, + "right_line": 653 + }, + { + "left_line": -1, + "right_line": 654 + }, + { + "left_line": -1, + "right_line": 655 + }, + { + "left_line": -1, + "right_line": 656 + }, + { + "left_line": -1, + "right_line": 657 + }, + { + "left_line": -1, + "right_line": 658 + }, + { + "left_line": -1, + "right_line": 659 + }, + { + "left_line": -1, + "right_line": 660 + }, + { + "left_line": -1, + "right_line": 661 + }, + { + "left_line": -1, + "right_line": 662 + }, + { + "left_line": -1, + "right_line": 663 + }, + { + "left_line": -1, + "right_line": 664 + }, + { + "left_line": -1, + "right_line": 665 + }, + { + "left_line": -1, + "right_line": 666 + }, + { + "left_line": -1, + "right_line": 667 + }, + { + "left_line": -1, + "right_line": 668 + }, + { + "left_line": -1, + "right_line": 669 + }, + { + "left_line": -1, + "right_line": 670 + }, + { + "left_line": -1, + "right_line": 671 + }, + { + "left_line": -1, + "right_line": 672 + }, + { + "left_line": -1, + "right_line": 673 + }, + { + "left_line": -1, + "right_line": 674 + }, + { + "left_line": -1, + "right_line": 675 + }, + { + "left_line": -1, + "right_line": 676 + }, + { + "left_line": -1, + "right_line": 677 + }, + { + "left_line": -1, + "right_line": 678 + }, + { + "left_line": -1, + "right_line": 679 + }, + { + "left_line": -1, + "right_line": 680 + }, + { + "left_line": -1, + "right_line": 681 + }, + { + "left_line": -1, + "right_line": 682 + }, + { + "left_line": -1, + "right_line": 683 + }, + { + "left_line": -1, + "right_line": 684 + }, + { + "left_line": -1, + "right_line": 685 + }, + { + "left_line": -1, + "right_line": 686 + }, + { + "left_line": -1, + "right_line": 687 + }, + { + "left_line": -1, + "right_line": 688 + }, + { + "left_line": -1, + "right_line": 689 + }, + { + "left_line": -1, + "right_line": 690 + }, + { + "left_line": -1, + "right_line": 691 + }, + { + "left_line": -1, + "right_line": 692 + }, + { + "left_line": -1, + "right_line": 693 + }, + { + "left_line": -1, + "right_line": 694 + }, + { + "left_line": -1, + "right_line": 695 + }, + { + "left_line": -1, + "right_line": 696 + }, + { + "left_line": -1, + "right_line": 697 + }, + { + "left_line": -1, + "right_line": 698 + }, + { + "left_line": -1, + "right_line": 699 + }, + { + "left_line": -1, + "right_line": 700 + }, + { + "left_line": -1, + "right_line": 701 + }, + { + "left_line": -1, + "right_line": 702 + }, + { + "left_line": -1, + "right_line": 703 + }, + { + "left_line": -1, + "right_line": 704 + }, + { + "left_line": -1, + "right_line": 705 + }, + { + "left_line": -1, + "right_line": 706 + }, + { + "left_line": -1, + "right_line": 707 + }, + { + "left_line": -1, + "right_line": 708 + }, + { + "left_line": -1, + "right_line": 709 + }, + { + "left_line": -1, + "right_line": 710 + }, + { + "left_line": -1, + "right_line": 711 + }, + { + "left_line": -1, + "right_line": 712 + }, + { + "left_line": -1, + "right_line": 713 + }, + { + "left_line": -1, + "right_line": 714 + }, + { + "left_line": -1, + "right_line": 715 + }, + { + "left_line": -1, + "right_line": 716 + }, + { + "left_line": -1, + "right_line": 717 + }, + { + "left_line": -1, + "right_line": 718 + }, + { + "left_line": -1, + "right_line": 719 + }, + { + "left_line": -1, + "right_line": 720 + }, + { + "left_line": -1, + "right_line": 721 + }, + { + "left_line": -1, + "right_line": 722 + }, + { + "left_line": -1, + "right_line": 723 + }, + { + "left_line": -1, + "right_line": 724 + }, + { + "left_line": -1, + "right_line": 725 + }, + { + "left_line": -1, + "right_line": 726 + }, + { + "left_line": -1, + "right_line": 727 + }, + { + "left_line": -1, + "right_line": 728 + }, + { + "left_line": -1, + "right_line": 729 + }, + { + "left_line": -1, + "right_line": 730 + }, + { + "left_line": -1, + "right_line": 731 + }, + { + "left_line": -1, + "right_line": 732 + }, + { + "left_line": -1, + "right_line": 733 + }, + { + "left_line": -1, + "right_line": 734 + }, + { + "left_line": -1, + "right_line": 735 + }, + { + "left_line": -1, + "right_line": 736 + }, + { + "left_line": -1, + "right_line": 737 + }, + { + "left_line": -1, + "right_line": 738 + }, + { + "left_line": -1, + "right_line": 739 + }, + { + "left_line": -1, + "right_line": 740 + }, + { + "left_line": -1, + "right_line": 741 + }, + { + "left_line": -1, + "right_line": 742 + }, + { + "left_line": -1, + "right_line": 743 + }, + { + "left_line": -1, + "right_line": 744 + }, + { + "left_line": -1, + "right_line": 745 + }, + { + "left_line": -1, + "right_line": 746 + }, + { + "left_line": -1, + "right_line": 747 + }, + { + "left_line": -1, + "right_line": 748 + }, + { + "left_line": -1, + "right_line": 749 + }, + { + "left_line": -1, + "right_line": 750 + }, + { + "left_line": -1, + "right_line": 751 + }, + { + "left_line": -1, + "right_line": 752 + }, + { + "left_line": -1, + "right_line": 753 + }, + { + "left_line": -1, + "right_line": 754 + }, + { + "left_line": -1, + "right_line": 755 + }, + { + "left_line": -1, + "right_line": 756 + }, + { + "left_line": -1, + "right_line": 757 + }, + { + "left_line": -1, + "right_line": 758 + }, + { + "left_line": -1, + "right_line": 759 + }, + { + "left_line": -1, + "right_line": 760 + }, + { + "left_line": -1, + "right_line": 761 + }, + { + "left_line": -1, + "right_line": 762 + }, + { + "left_line": -1, + "right_line": 763 + }, + { + "left_line": -1, + "right_line": 764 + }, + { + "left_line": -1, + "right_line": 765 + }, + { + "left_line": -1, + "right_line": 766 + }, + { + "left_line": -1, + "right_line": 767 + }, + { + "left_line": -1, + "right_line": 768 + }, + { + "left_line": -1, + "right_line": 769 + }, + { + "left_line": -1, + "right_line": 770 + }, + { + "left_line": -1, + "right_line": 771 + }, + { + "left_line": -1, + "right_line": 772 + }, + { + "left_line": -1, + "right_line": 773 + }, + { + "left_line": -1, + "right_line": 774 + }, + { + "left_line": -1, + "right_line": 775 + }, + { + "left_line": -1, + "right_line": 776 + }, + { + "left_line": -1, + "right_line": 777 + }, + { + "left_line": -1, + "right_line": 778 + }, + { + "left_line": -1, + "right_line": 779 + }, + { + "left_line": -1, + "right_line": 780 + }, + { + "left_line": -1, + "right_line": 781 + }, + { + "left_line": -1, + "right_line": 782 + }, + { + "left_line": -1, + "right_line": 783 + }, + { + "left_line": -1, + "right_line": 784 + }, + { + "left_line": -1, + "right_line": 785 + }, + { + "left_line": -1, + "right_line": 786 + }, + { + "left_line": -1, + "right_line": 787 + }, + { + "left_line": -1, + "right_line": 788 + }, + { + "left_line": -1, + "right_line": 789 + }, + { + "left_line": -1, + "right_line": 790 + }, + { + "left_line": -1, + "right_line": 791 + }, + { + "left_line": -1, + "right_line": 792 + }, + { + "left_line": -1, + "right_line": 793 + }, + { + "left_line": -1, + "right_line": 794 + }, + { + "left_line": -1, + "right_line": 795 + }, + { + "left_line": -1, + "right_line": 796 + }, + { + "left_line": -1, + "right_line": 797 + }, + { + "left_line": -1, + "right_line": 798 + }, + { + "left_line": -1, + "right_line": 799 + }, + { + "left_line": -1, + "right_line": 800 + }, + { + "left_line": -1, + "right_line": 801 + }, + { + "left_line": -1, + "right_line": 802 + }, + { + "left_line": -1, + "right_line": 803 + }, + { + "left_line": -1, + "right_line": 804 + }, + { + "left_line": -1, + "right_line": 805 + }, + { + "left_line": -1, + "right_line": 806 + }, + { + "left_line": -1, + "right_line": 807 + }, + { + "left_line": -1, + "right_line": 808 + }, + { + "left_line": -1, + "right_line": 809 + }, + { + "left_line": -1, + "right_line": 810 + }, + { + "left_line": -1, + "right_line": 811 + }, + { + "left_line": -1, + "right_line": 812 + }, + { + "left_line": -1, + "right_line": 813 + }, + { + "left_line": -1, + "right_line": 814 + }, + { + "left_line": -1, + "right_line": 815 + }, + { + "left_line": -1, + "right_line": 816 + }, + { + "left_line": -1, + "right_line": 817 + }, + { + "left_line": -1, + "right_line": 818 + }, + { + "left_line": -1, + "right_line": 819 + }, + { + "left_line": -1, + "right_line": 820 + }, + { + "left_line": -1, + "right_line": 821 + }, + { + "left_line": -1, + "right_line": 822 + }, + { + "left_line": -1, + "right_line": 823 + }, + { + "left_line": -1, + "right_line": 824 + }, + { + "left_line": -1, + "right_line": 825 + }, + { + "left_line": -1, + "right_line": 826 + }, + { + "left_line": -1, + "right_line": 827 + }, + { + "left_line": -1, + "right_line": 828 + }, + { + "left_line": -1, + "right_line": 829 + }, + { + "left_line": -1, + "right_line": 830 + }, + { + "left_line": -1, + "right_line": 831 + }, + { + "left_line": -1, + "right_line": 832 + }, + { + "left_line": -1, + "right_line": 833 + }, + { + "left_line": -1, + "right_line": 834 + }, + { + "left_line": -1, + "right_line": 835 + }, + { + "left_line": -1, + "right_line": 836 + }, + { + "left_line": -1, + "right_line": 837 + }, + { + "left_line": -1, + "right_line": 838 + }, + { + "left_line": -1, + "right_line": 839 + }, + { + "left_line": -1, + "right_line": 840 + }, + { + "left_line": -1, + "right_line": 841 + }, + { + "left_line": -1, + "right_line": 842 + }, + { + "left_line": -1, + "right_line": 843 + }, + { + "left_line": -1, + "right_line": 844 + }, + { + "left_line": -1, + "right_line": 845 + }, + { + "left_line": -1, + "right_line": 846 + }, + { + "left_line": -1, + "right_line": 847 + }, + { + "left_line": -1, + "right_line": 848 + }, + { + "left_line": -1, + "right_line": 849 + }, + { + "left_line": -1, + "right_line": 850 + }, + { + "left_line": -1, + "right_line": 851 + }, + { + "left_line": -1, + "right_line": 852 + }, + { + "left_line": -1, + "right_line": 853 + }, + { + "left_line": -1, + "right_line": 854 + }, + { + "left_line": -1, + "right_line": 855 + }, + { + "left_line": -1, + "right_line": 856 + }, + { + "left_line": -1, + "right_line": 857 + }, + { + "left_line": -1, + "right_line": 858 + }, + { + "left_line": -1, + "right_line": 859 + }, + { + "left_line": -1, + "right_line": 860 + }, + { + "left_line": -1, + "right_line": 861 + }, + { + "left_line": -1, + "right_line": 862 + }, + { + "left_line": -1, + "right_line": 863 + }, + { + "left_line": -1, + "right_line": 864 + }, + { + "left_line": -1, + "right_line": 865 + }, + { + "left_line": -1, + "right_line": 866 + }, + { + "left_line": -1, + "right_line": 867 + }, + { + "left_line": -1, + "right_line": 868 + }, + { + "left_line": -1, + "right_line": 869 + }, + { + "left_line": -1, + "right_line": 870 + }, + { + "left_line": -1, + "right_line": 871 + }, + { + "left_line": -1, + "right_line": 872 + }, + { + "left_line": -1, + "right_line": 873 + }, + { + "left_line": -1, + "right_line": 874 + }, + { + "left_line": -1, + "right_line": 875 + }, + { + "left_line": -1, + "right_line": 876 + }, + { + "left_line": -1, + "right_line": 877 + }, + { + "left_line": -1, + "right_line": 878 + }, + { + "left_line": -1, + "right_line": 879 + }, + { + "left_line": -1, + "right_line": 880 + }, + { + "left_line": -1, + "right_line": 881 + }, + { + "left_line": -1, + "right_line": 882 + }, + { + "left_line": -1, + "right_line": 883 + }, + { + "left_line": -1, + "right_line": 884 + }, + { + "left_line": -1, + "right_line": 885 + }, + { + "left_line": -1, + "right_line": 886 + }, + { + "left_line": -1, + "right_line": 887 + }, + { + "left_line": -1, + "right_line": 888 + }, + { + "left_line": -1, + "right_line": 889 + }, + { + "left_line": -1, + "right_line": 890 + }, + { + "left_line": -1, + "right_line": 891 + }, + { + "left_line": -1, + "right_line": 892 + }, + { + "left_line": -1, + "right_line": 893 + }, + { + "left_line": -1, + "right_line": 894 + }, + { + "left_line": -1, + "right_line": 895 + }, + { + "left_line": -1, + "right_line": 896 + }, + { + "left_line": -1, + "right_line": 897 + }, + { + "left_line": -1, + "right_line": 898 + }, + { + "left_line": -1, + "right_line": 899 + }, + { + "left_line": -1, + "right_line": 900 + }, + { + "left_line": -1, + "right_line": 901 + }, + { + "left_line": -1, + "right_line": 902 + }, + { + "left_line": -1, + "right_line": 903 + }, + { + "left_line": -1, + "right_line": 904 + }, + { + "left_line": -1, + "right_line": 905 + }, + { + "left_line": -1, + "right_line": 906 + }, + { + "left_line": -1, + "right_line": 907 + }, + { + "left_line": -1, + "right_line": 908 + }, + { + "left_line": -1, + "right_line": 909 + }, + { + "left_line": -1, + "right_line": 910 + }, + { + "left_line": -1, + "right_line": 911 + }, + { + "left_line": -1, + "right_line": 912 + }, + { + "left_line": -1, + "right_line": 913 + }, + { + "left_line": -1, + "right_line": 914 + }, + { + "left_line": -1, + "right_line": 915 + }, + { + "left_line": -1, + "right_line": 916 + }, + { + "left_line": -1, + "right_line": 917 + }, + { + "left_line": -1, + "right_line": 918 + }, + { + "left_line": -1, + "right_line": 919 + }, + { + "left_line": -1, + "right_line": 920 + }, + { + "left_line": -1, + "right_line": 921 + }, + { + "left_line": -1, + "right_line": 922 + }, + { + "left_line": -1, + "right_line": 923 + }, + { + "left_line": -1, + "right_line": 924 + }, + { + "left_line": -1, + "right_line": 925 + }, + { + "left_line": -1, + "right_line": 926 + }, + { + "left_line": -1, + "right_line": 927 + }, + { + "left_line": -1, + "right_line": 928 + }, + { + "left_line": -1, + "right_line": 929 + }, + { + "left_line": -1, + "right_line": 930 + }, + { + "left_line": -1, + "right_line": 931 + }, + { + "left_line": -1, + "right_line": 932 + }, + { + "left_line": -1, + "right_line": 933 + }, + { + "left_line": -1, + "right_line": 934 + }, + { + "left_line": -1, + "right_line": 935 + }, + { + "left_line": -1, + "right_line": 936 + }, + { + "left_line": -1, + "right_line": 937 + }, + { + "left_line": -1, + "right_line": 938 + }, + { + "left_line": -1, + "right_line": 939 + }, + { + "left_line": -1, + "right_line": 940 + }, + { + "left_line": -1, + "right_line": 941 + }, + { + "left_line": -1, + "right_line": 942 + }, + { + "left_line": -1, + "right_line": 943 + }, + { + "left_line": -1, + "right_line": 944 + }, + { + "left_line": -1, + "right_line": 945 + }, + { + "left_line": -1, + "right_line": 946 + }, + { + "left_line": -1, + "right_line": 947 + }, + { + "left_line": -1, + "right_line": 948 + }, + { + "left_line": -1, + "right_line": 949 + }, + { + "left_line": -1, + "right_line": 950 + }, + { + "left_line": -1, + "right_line": 951 + }, + { + "left_line": -1, + "right_line": 952 + }, + { + "left_line": -1, + "right_line": 953 + }, + { + "left_line": -1, + "right_line": 954 + }, + { + "left_line": -1, + "right_line": 955 + }, + { + "left_line": -1, + "right_line": 956 + }, + { + "left_line": -1, + "right_line": 957 + }, + { + "left_line": -1, + "right_line": 958 + }, + { + "left_line": -1, + "right_line": 959 + }, + { + "left_line": -1, + "right_line": 960 + }, + { + "left_line": -1, + "right_line": 961 + }, + { + "left_line": -1, + "right_line": 962 + }, + { + "left_line": -1, + "right_line": 963 + }, + { + "left_line": -1, + "right_line": 964 + }, + { + "left_line": -1, + "right_line": 965 + }, + { + "left_line": -1, + "right_line": 966 + }, + { + "left_line": -1, + "right_line": 967 + }, + { + "left_line": -1, + "right_line": 968 + }, + { + "left_line": -1, + "right_line": 969 + }, + { + "left_line": -1, + "right_line": 970 + }, + { + "left_line": -1, + "right_line": 971 + }, + { + "left_line": -1, + "right_line": 972 + }, + { + "left_line": -1, + "right_line": 973 + }, + { + "left_line": -1, + "right_line": 974 + }, + { + "left_line": -1, + "right_line": 975 + }, + { + "left_line": -1, + "right_line": 976 + }, + { + "left_line": -1, + "right_line": 977 + }, + { + "left_line": -1, + "right_line": 978 + }, + { + "left_line": -1, + "right_line": 979 + }, + { + "left_line": -1, + "right_line": 980 + }, + { + "left_line": -1, + "right_line": 981 + }, + { + "left_line": -1, + "right_line": 982 + }, + { + "left_line": -1, + "right_line": 983 + }, + { + "left_line": -1, + "right_line": 984 + }, + { + "left_line": -1, + "right_line": 985 + }, + { + "left_line": -1, + "right_line": 986 + }, + { + "left_line": -1, + "right_line": 987 + }, + { + "left_line": -1, + "right_line": 988 + }, + { + "left_line": -1, + "right_line": 989 + }, + { + "left_line": -1, + "right_line": 990 + }, + { + "left_line": -1, + "right_line": 991 + }, + { + "left_line": -1, + "right_line": 992 + }, + { + "left_line": -1, + "right_line": 993 + }, + { + "left_line": -1, + "right_line": 994 + }, + { + "left_line": -1, + "right_line": 995 + }, + { + "left_line": -1, + "right_line": 996 + }, + { + "left_line": -1, + "right_line": 997 + }, + { + "left_line": -1, + "right_line": 998 + }, + { + "left_line": -1, + "right_line": 999 + }, + { + "left_line": -1, + "right_line": 1000 + }, + { + "left_line": -1, + "right_line": 1001 + }, + { + "left_line": -1, + "right_line": 1002 + }, + { + "left_line": -1, + "right_line": 1003 + }, + { + "left_line": -1, + "right_line": 1004 + }, + { + "left_line": -1, + "right_line": 1005 + }, + { + "left_line": -1, + "right_line": 1006 + }, + { + "left_line": -1, + "right_line": 1007 + }, + { + "left_line": -1, + "right_line": 1008 + }, + { + "left_line": -1, + "right_line": 1009 + }, + { + "left_line": -1, + "right_line": 1010 + }, + { + "left_line": -1, + "right_line": 1011 + }, + { + "left_line": -1, + "right_line": 1012 + }, + { + "left_line": -1, + "right_line": 1013 + }, + { + "left_line": -1, + "right_line": 1014 + }, + { + "left_line": -1, + "right_line": 1015 + }, + { + "left_line": -1, + "right_line": 1016 + }, + { + "left_line": -1, + "right_line": 1017 + }, + { + "left_line": -1, + "right_line": 1018 + }, + { + "left_line": -1, + "right_line": 1019 + }, + { + "left_line": -1, + "right_line": 1020 + }, + { + "left_line": -1, + "right_line": 1021 + }, + { + "left_line": -1, + "right_line": 1022 + }, + { + "left_line": -1, + "right_line": 1023 + }, + { + "left_line": -1, + "right_line": 1024 + }, + { + "left_line": -1, + "right_line": 1025 + }, + { + "left_line": -1, + "right_line": 1026 + }, + { + "left_line": -1, + "right_line": 1027 + }, + { + "left_line": -1, + "right_line": 1028 + }, + { + "left_line": -1, + "right_line": 1029 + }, + { + "left_line": -1, + "right_line": 1030 + }, + { + "left_line": -1, + "right_line": 1031 + }, + { + "left_line": -1, + "right_line": 1032 + }, + { + "left_line": -1, + "right_line": 1033 + }, + { + "left_line": -1, + "right_line": 1034 + }, + { + "left_line": -1, + "right_line": 1035 + }, + { + "left_line": -1, + "right_line": 1036 + }, + { + "left_line": -1, + "right_line": 1037 + }, + { + "left_line": -1, + "right_line": 1038 + }, + { + "left_line": -1, + "right_line": 1039 + }, + { + "left_line": -1, + "right_line": 1040 + }, + { + "left_line": -1, + "right_line": 1041 + }, + { + "left_line": -1, + "right_line": 1042 + }, + { + "left_line": -1, + "right_line": 1043 + }, + { + "left_line": -1, + "right_line": 1044 + }, + { + "left_line": -1, + "right_line": 1045 + }, + { + "left_line": -1, + "right_line": 1046 + }, + { + "left_line": -1, + "right_line": 1047 + }, + { + "left_line": -1, + "right_line": 1048 + }, + { + "left_line": -1, + "right_line": 1049 + }, + { + "left_line": -1, + "right_line": 1050 + }, + { + "left_line": -1, + "right_line": 1051 + }, + { + "left_line": -1, + "right_line": 1052 + }, + { + "left_line": -1, + "right_line": 1053 + }, + { + "left_line": -1, + "right_line": 1054 + }, + { + "left_line": -1, + "right_line": 1055 + }, + { + "left_line": -1, + "right_line": 1056 + }, + { + "left_line": -1, + "right_line": 1057 + }, + { + "left_line": -1, + "right_line": 1058 + }, + { + "left_line": -1, + "right_line": 1059 + }, + { + "left_line": -1, + "right_line": 1060 + }, + { + "left_line": -1, + "right_line": 1061 + }, + { + "left_line": -1, + "right_line": 1062 + }, + { + "left_line": -1, + "right_line": 1063 + }, + { + "left_line": -1, + "right_line": 1064 + }, + { + "left_line": -1, + "right_line": 1065 + }, + { + "left_line": -1, + "right_line": 1066 + }, + { + "left_line": -1, + "right_line": 1067 + }, + { + "left_line": -1, + "right_line": 1068 + }, + { + "left_line": -1, + "right_line": 1069 + }, + { + "left_line": -1, + "right_line": 1070 + }, + { + "left_line": -1, + "right_line": 1071 + }, + { + "left_line": -1, + "right_line": 1072 + }, + { + "left_line": -1, + "right_line": 1073 + }, + { + "left_line": -1, + "right_line": 1074 + }, + { + "left_line": -1, + "right_line": 1075 + }, + { + "left_line": -1, + "right_line": 1076 + }, + { + "left_line": -1, + "right_line": 1077 + }, + { + "left_line": -1, + "right_line": 1078 + }, + { + "left_line": -1, + "right_line": 1079 + }, + { + "left_line": -1, + "right_line": 1080 + }, + { + "left_line": -1, + "right_line": 1081 + }, + { + "left_line": -1, + "right_line": 1082 + }, + { + "left_line": -1, + "right_line": 1083 + }, + { + "left_line": -1, + "right_line": 1084 + }, + { + "left_line": -1, + "right_line": 1085 + }, + { + "left_line": -1, + "right_line": 1086 + }, + { + "left_line": -1, + "right_line": 1087 + }, + { + "left_line": -1, + "right_line": 1088 + }, + { + "left_line": -1, + "right_line": 1089 + }, + { + "left_line": -1, + "right_line": 1090 + }, + { + "left_line": -1, + "right_line": 1091 + }, + { + "left_line": -1, + "right_line": 1092 + }, + { + "left_line": -1, + "right_line": 1093 + }, + { + "left_line": -1, + "right_line": 1094 + }, + { + "left_line": -1, + "right_line": 1095 + }, + { + "left_line": -1, + "right_line": 1096 + }, + { + "left_line": -1, + "right_line": 1097 + }, + { + "left_line": -1, + "right_line": 1098 + }, + { + "left_line": -1, + "right_line": 1099 + }, + { + "left_line": -1, + "right_line": 1100 + }, + { + "left_line": -1, + "right_line": 1101 + }, + { + "left_line": -1, + "right_line": 1102 + }, + { + "left_line": -1, + "right_line": 1103 + }, + { + "left_line": -1, + "right_line": 1104 + }, + { + "left_line": -1, + "right_line": 1105 + }, + { + "left_line": -1, + "right_line": 1106 + }, + { + "left_line": -1, + "right_line": 1107 + }, + { + "left_line": -1, + "right_line": 1108 + }, + { + "left_line": -1, + "right_line": 1109 + }, + { + "left_line": -1, + "right_line": 1110 + }, + { + "left_line": -1, + "right_line": 1111 + }, + { + "left_line": -1, + "right_line": 1112 + }, + { + "left_line": -1, + "right_line": 1113 + }, + { + "left_line": -1, + "right_line": 1114 + }, + { + "left_line": -1, + "right_line": 1115 + }, + { + "left_line": -1, + "right_line": 1116 + }, + { + "left_line": -1, + "right_line": 1117 + }, + { + "left_line": -1, + "right_line": 1118 + }, + { + "left_line": -1, + "right_line": 1119 + }, + { + "left_line": -1, + "right_line": 1120 + }, + { + "left_line": -1, + "right_line": 1121 + }, + { + "left_line": -1, + "right_line": 1122 + }, + { + "left_line": -1, + "right_line": 1123 + }, + { + "left_line": -1, + "right_line": 1124 + }, + { + "left_line": -1, + "right_line": 1125 + }, + { + "left_line": -1, + "right_line": 1126 + }, + { + "left_line": -1, + "right_line": 1127 + }, + { + "left_line": -1, + "right_line": 1128 + }, + { + "left_line": -1, + "right_line": 1129 + }, + { + "left_line": -1, + "right_line": 1130 + }, + { + "left_line": -1, + "right_line": 1131 + }, + { + "left_line": -1, + "right_line": 1132 + }, + { + "left_line": -1, + "right_line": 1133 + }, + { + "left_line": -1, + "right_line": 1134 + }, + { + "left_line": -1, + "right_line": 1135 + }, + { + "left_line": -1, + "right_line": 1136 + }, + { + "left_line": -1, + "right_line": 1137 + }, + { + "left_line": -1, + "right_line": 1138 + }, + { + "left_line": -1, + "right_line": 1139 + }, + { + "left_line": -1, + "right_line": 1140 + }, + { + "left_line": -1, + "right_line": 1141 + }, + { + "left_line": -1, + "right_line": 1142 + }, + { + "left_line": -1, + "right_line": 1143 + }, + { + "left_line": -1, + "right_line": 1144 + }, + { + "left_line": -1, + "right_line": 1145 + }, + { + "left_line": -1, + "right_line": 1146 + }, + { + "left_line": -1, + "right_line": 1147 + }, + { + "left_line": -1, + "right_line": 1148 + }, + { + "left_line": -1, + "right_line": 1149 + }, + { + "left_line": -1, + "right_line": 1150 + }, + { + "left_line": -1, + "right_line": 1151 + }, + { + "left_line": -1, + "right_line": 1152 + }, + { + "left_line": -1, + "right_line": 1153 + }, + { + "left_line": -1, + "right_line": 1154 + }, + { + "left_line": -1, + "right_line": 1155 + }, + { + "left_line": -1, + "right_line": 1156 + }, + { + "left_line": -1, + "right_line": 1157 + }, + { + "left_line": -1, + "right_line": 1158 + }, + { + "left_line": -1, + "right_line": 1159 + }, + { + "left_line": -1, + "right_line": 1160 + }, + { + "left_line": -1, + "right_line": 1161 + }, + { + "left_line": -1, + "right_line": 1162 + }, + { + "left_line": -1, + "right_line": 1163 + }, + { + "left_line": -1, + "right_line": 1164 + }, + { + "left_line": -1, + "right_line": 1165 + }, + { + "left_line": -1, + "right_line": 1166 + }, + { + "left_line": -1, + "right_line": 1167 + }, + { + "left_line": -1, + "right_line": 1168 + }, + { + "left_line": -1, + "right_line": 1169 + }, + { + "left_line": -1, + "right_line": 1170 + }, + { + "left_line": -1, + "right_line": 1171 + }, + { + "left_line": -1, + "right_line": 1172 + }, + { + "left_line": -1, + "right_line": 1173 + }, + { + "left_line": -1, + "right_line": 1174 + }, + { + "left_line": -1, + "right_line": 1175 + }, + { + "left_line": -1, + "right_line": 1176 + }, + { + "left_line": -1, + "right_line": 1177 + }, + { + "left_line": -1, + "right_line": 1178 + }, + { + "left_line": -1, + "right_line": 1179 + }, + { + "left_line": -1, + "right_line": 1180 + }, + { + "left_line": -1, + "right_line": 1181 + }, + { + "left_line": -1, + "right_line": 1182 + }, + { + "left_line": -1, + "right_line": 1183 + }, + { + "left_line": -1, + "right_line": 1184 + }, + { + "left_line": -1, + "right_line": 1185 + }, + { + "left_line": -1, + "right_line": 1186 + }, + { + "left_line": -1, + "right_line": 1187 + }, + { + "left_line": -1, + "right_line": 1188 + }, + { + "left_line": -1, + "right_line": 1189 + }, + { + "left_line": -1, + "right_line": 1190 + }, + { + "left_line": -1, + "right_line": 1191 + }, + { + "left_line": -1, + "right_line": 1192 + }, + { + "left_line": -1, + "right_line": 1193 + }, + { + "left_line": -1, + "right_line": 1194 + }, + { + "left_line": -1, + "right_line": 1195 + }, + { + "left_line": -1, + "right_line": 1196 + }, + { + "left_line": -1, + "right_line": 1197 + }, + { + "left_line": -1, + "right_line": 1198 + }, + { + "left_line": -1, + "right_line": 1199 + }, + { + "left_line": -1, + "right_line": 1200 + }, + { + "left_line": -1, + "right_line": 1201 + }, + { + "left_line": -1, + "right_line": 1202 + }, + { + "left_line": -1, + "right_line": 1203 + }, + { + "left_line": -1, + "right_line": 1204 + }, + { + "left_line": -1, + "right_line": 1205 + }, + { + "left_line": -1, + "right_line": 1206 + }, + { + "left_line": -1, + "right_line": 1207 + }, + { + "left_line": -1, + "right_line": 1208 + }, + { + "left_line": -1, + "right_line": 1209 + }, + { + "left_line": -1, + "right_line": 1210 + }, + { + "left_line": -1, + "right_line": 1211 + }, + { + "left_line": -1, + "right_line": 1212 + }, + { + "left_line": -1, + "right_line": 1213 + }, + { + "left_line": -1, + "right_line": 1214 + }, + { + "left_line": -1, + "right_line": 1215 + }, + { + "left_line": -1, + "right_line": 1216 + }, + { + "left_line": -1, + "right_line": 1217 + }, + { + "left_line": -1, + "right_line": 1218 + }, + { + "left_line": -1, + "right_line": 1219 + }, + { + "left_line": -1, + "right_line": 1220 + }, + { + "left_line": -1, + "right_line": 1221 + }, + { + "left_line": -1, + "right_line": 1222 + }, + { + "left_line": -1, + "right_line": 1223 + }, + { + "left_line": -1, + "right_line": 1224 + }, + { + "left_line": -1, + "right_line": 1225 + }, + { + "left_line": -1, + "right_line": 1226 + }, + { + "left_line": -1, + "right_line": 1227 + }, + { + "left_line": -1, + "right_line": 1228 + }, + { + "left_line": -1, + "right_line": 1229 + }, + { + "left_line": -1, + "right_line": 1230 + }, + { + "left_line": -1, + "right_line": 1231 + }, + { + "left_line": -1, + "right_line": 1232 + }, + { + "left_line": -1, + "right_line": 1233 + }, + { + "left_line": -1, + "right_line": 1234 + }, + { + "left_line": -1, + "right_line": 1235 + }, + { + "left_line": -1, + "right_line": 1236 + }, + { + "left_line": -1, + "right_line": 1237 + }, + { + "left_line": -1, + "right_line": 1238 + }, + { + "left_line": -1, + "right_line": 1239 + }, + { + "left_line": -1, + "right_line": 1240 + }, + { + "left_line": -1, + "right_line": 1241 + }, + { + "left_line": -1, + "right_line": 1242 + }, + { + "left_line": -1, + "right_line": 1243 + }, + { + "left_line": -1, + "right_line": 1244 + }, + { + "left_line": -1, + "right_line": 1245 + }, + { + "left_line": -1, + "right_line": 1246 + }, + { + "left_line": -1, + "right_line": 1247 + }, + { + "left_line": -1, + "right_line": 1248 + }, + { + "left_line": -1, + "right_line": 1249 + }, + { + "left_line": -1, + "right_line": 1250 + }, + { + "left_line": -1, + "right_line": 1251 + }, + { + "left_line": -1, + "right_line": 1252 + }, + { + "left_line": -1, + "right_line": 1253 + }, + { + "left_line": -1, + "right_line": 1254 + }, + { + "left_line": -1, + "right_line": 1255 + }, + { + "left_line": -1, + "right_line": 1256 + }, + { + "left_line": -1, + "right_line": 1257 + }, + { + "left_line": -1, + "right_line": 1258 + }, + { + "left_line": -1, + "right_line": 1259 + }, + { + "left_line": -1, + "right_line": 1260 + }, + { + "left_line": -1, + "right_line": 1261 + }, + { + "left_line": -1, + "right_line": 1262 + }, + { + "left_line": -1, + "right_line": 1263 + }, + { + "left_line": -1, + "right_line": 1264 + }, + { + "left_line": -1, + "right_line": 1265 + }, + { + "left_line": -1, + "right_line": 1266 + }, + { + "left_line": -1, + "right_line": 1267 + }, + { + "left_line": -1, + "right_line": 1268 + }, + { + "left_line": -1, + "right_line": 1269 + }, + { + "left_line": -1, + "right_line": 1270 + }, + { + "left_line": -1, + "right_line": 1271 + }, + { + "left_line": -1, + "right_line": 1272 + }, + { + "left_line": -1, + "right_line": 1273 + }, + { + "left_line": -1, + "right_line": 1274 + }, + { + "left_line": -1, + "right_line": 1275 + }, + { + "left_line": -1, + "right_line": 1276 + }, + { + "left_line": -1, + "right_line": 1277 + }, + { + "left_line": -1, + "right_line": 1278 + }, + { + "left_line": -1, + "right_line": 1279 + }, + { + "left_line": -1, + "right_line": 1280 + }, + { + "left_line": -1, + "right_line": 1281 + }, + { + "left_line": -1, + "right_line": 1282 + }, + { + "left_line": -1, + "right_line": 1283 + }, + { + "left_line": -1, + "right_line": 1284 + }, + { + "left_line": -1, + "right_line": 1285 + }, + { + "left_line": -1, + "right_line": 1286 + }, + { + "left_line": -1, + "right_line": 1287 + }, + { + "left_line": -1, + "right_line": 1288 + }, + { + "left_line": -1, + "right_line": 1289 + }, + { + "left_line": -1, + "right_line": 1290 + }, + { + "left_line": -1, + "right_line": 1291 + }, + { + "left_line": -1, + "right_line": 1292 + }, + { + "left_line": -1, + "right_line": 1293 + }, + { + "left_line": -1, + "right_line": 1294 + }, + { + "left_line": -1, + "right_line": 1295 + }, + { + "left_line": -1, + "right_line": 1296 + }, + { + "left_line": -1, + "right_line": 1297 + }, + { + "left_line": -1, + "right_line": 1298 + }, + { + "left_line": -1, + "right_line": 1299 + }, + { + "left_line": -1, + "right_line": 1300 + }, + { + "left_line": -1, + "right_line": 1301 + }, + { + "left_line": -1, + "right_line": 1302 + }, + { + "left_line": -1, + "right_line": 1303 + }, + { + "left_line": -1, + "right_line": 1304 + }, + { + "left_line": -1, + "right_line": 1305 + }, + { + "left_line": -1, + "right_line": 1306 + }, + { + "left_line": -1, + "right_line": 1307 + }, + { + "left_line": -1, + "right_line": 1308 + }, + { + "left_line": -1, + "right_line": 1309 + }, + { + "left_line": -1, + "right_line": 1310 + }, + { + "left_line": -1, + "right_line": 1311 + }, + { + "left_line": -1, + "right_line": 1312 + }, + { + "left_line": -1, + "right_line": 1313 + }, + { + "left_line": -1, + "right_line": 1314 + }, + { + "left_line": -1, + "right_line": 1315 + }, + { + "left_line": -1, + "right_line": 1316 + }, + { + "left_line": -1, + "right_line": 1317 + }, + { + "left_line": -1, + "right_line": 1318 + }, + { + "left_line": -1, + "right_line": 1319 + }, + { + "left_line": -1, + "right_line": 1320 + }, + { + "left_line": -1, + "right_line": 1321 + }, + { + "left_line": -1, + "right_line": 1322 + }, + { + "left_line": -1, + "right_line": 1323 + }, + { + "left_line": -1, + "right_line": 1324 + }, + { + "left_line": -1, + "right_line": 1325 + }, + { + "left_line": -1, + "right_line": 1326 + }, + { + "left_line": -1, + "right_line": 1327 + }, + { + "left_line": -1, + "right_line": 1328 + }, + { + "left_line": -1, + "right_line": 1329 + }, + { + "left_line": -1, + "right_line": 1330 + }, + { + "left_line": -1, + "right_line": 1331 + }, + { + "left_line": -1, + "right_line": 1332 + }, + { + "left_line": -1, + "right_line": 1333 + }, + { + "left_line": -1, + "right_line": 1334 + }, + { + "left_line": -1, + "right_line": 1335 + }, + { + "left_line": -1, + "right_line": 1336 + }, + { + "left_line": -1, + "right_line": 1337 + }, + { + "left_line": -1, + "right_line": 1338 + }, + { + "left_line": -1, + "right_line": 1339 + }, + { + "left_line": -1, + "right_line": 1340 + }, + { + "left_line": -1, + "right_line": 1341 + }, + { + "left_line": -1, + "right_line": 1342 + }, + { + "left_line": -1, + "right_line": 1343 + }, + { + "left_line": -1, + "right_line": 1344 + }, + { + "left_line": -1, + "right_line": 1345 + }, + { + "left_line": -1, + "right_line": 1346 + }, + { + "left_line": -1, + "right_line": 1347 + }, + { + "left_line": -1, + "right_line": 1348 + }, + { + "left_line": -1, + "right_line": 1349 + }, + { + "left_line": -1, + "right_line": 1350 + }, + { + "left_line": -1, + "right_line": 1351 + }, + { + "left_line": -1, + "right_line": 1352 + }, + { + "left_line": -1, + "right_line": 1353 + }, + { + "left_line": -1, + "right_line": 1354 + }, + { + "left_line": -1, + "right_line": 1355 + }, + { + "left_line": -1, + "right_line": 1356 + }, + { + "left_line": -1, + "right_line": 1357 + }, + { + "left_line": -1, + "right_line": 1358 + }, + { + "left_line": -1, + "right_line": 1359 + }, + { + "left_line": -1, + "right_line": 1360 + }, + { + "left_line": -1, + "right_line": 1361 + }, + { + "left_line": -1, + "right_line": 1362 + }, + { + "left_line": -1, + "right_line": 1363 + }, + { + "left_line": -1, + "right_line": 1364 + }, + { + "left_line": -1, + "right_line": 1365 + }, + { + "left_line": -1, + "right_line": 1366 + }, + { + "left_line": -1, + "right_line": 1367 + }, + { + "left_line": -1, + "right_line": 1368 + }, + { + "left_line": -1, + "right_line": 1369 + }, + { + "left_line": -1, + "right_line": 1370 + }, + { + "left_line": -1, + "right_line": 1371 + }, + { + "left_line": -1, + "right_line": 1372 + }, + { + "left_line": -1, + "right_line": 1373 + }, + { + "left_line": -1, + "right_line": 1374 + }, + { + "left_line": -1, + "right_line": 1375 + }, + { + "left_line": -1, + "right_line": 1376 + }, + { + "left_line": -1, + "right_line": 1377 + }, + { + "left_line": -1, + "right_line": 1378 + }, + { + "left_line": -1, + "right_line": 1379 + }, + { + "left_line": -1, + "right_line": 1380 + }, + { + "left_line": -1, + "right_line": 1381 + }, + { + "left_line": -1, + "right_line": 1382 + }, + { + "left_line": -1, + "right_line": 1383 + }, + { + "left_line": -1, + "right_line": 1384 + }, + { + "left_line": -1, + "right_line": 1385 + }, + { + "left_line": -1, + "right_line": 1386 + }, + { + "left_line": -1, + "right_line": 1387 + }, + { + "left_line": -1, + "right_line": 1388 + }, + { + "left_line": -1, + "right_line": 1389 + }, + { + "left_line": -1, + "right_line": 1390 + }, + { + "left_line": -1, + "right_line": 1391 + }, + { + "left_line": -1, + "right_line": 1392 + }, + { + "left_line": -1, + "right_line": 1393 + }, + { + "left_line": -1, + "right_line": 1394 + }, + { + "left_line": -1, + "right_line": 1395 + }, + { + "left_line": -1, + "right_line": 1396 + }, + { + "left_line": -1, + "right_line": 1397 + }, + { + "left_line": -1, + "right_line": 1398 + }, + { + "left_line": -1, + "right_line": 1399 + }, + { + "left_line": -1, + "right_line": 1400 + }, + { + "left_line": -1, + "right_line": 1401 + }, + { + "left_line": -1, + "right_line": 1402 + }, + { + "left_line": -1, + "right_line": 1403 + }, + { + "left_line": -1, + "right_line": 1404 + }, + { + "left_line": -1, + "right_line": 1405 + }, + { + "left_line": -1, + "right_line": 1406 + }, + { + "left_line": -1, + "right_line": 1407 + }, + { + "left_line": -1, + "right_line": 1408 + }, + { + "left_line": -1, + "right_line": 1409 + }, + { + "left_line": -1, + "right_line": 1410 + }, + { + "left_line": -1, + "right_line": 1411 + }, + { + "left_line": -1, + "right_line": 1412 + }, + { + "left_line": -1, + "right_line": 1413 + }, + { + "left_line": -1, + "right_line": 1414 + }, + { + "left_line": -1, + "right_line": 1415 + }, + { + "left_line": -1, + "right_line": 1416 + }, + { + "left_line": -1, + "right_line": 1417 + }, + { + "left_line": -1, + "right_line": 1418 + }, + { + "left_line": -1, + "right_line": 1419 + }, + { + "left_line": -1, + "right_line": 1420 + }, + { + "left_line": -1, + "right_line": 1421 + }, + { + "left_line": -1, + "right_line": 1422 + }, + { + "left_line": -1, + "right_line": 1423 + }, + { + "left_line": -1, + "right_line": 1424 + }, + { + "left_line": -1, + "right_line": 1425 + }, + { + "left_line": -1, + "right_line": 1426 + }, + { + "left_line": -1, + "right_line": 1427 + }, + { + "left_line": -1, + "right_line": 1428 + }, + { + "left_line": -1, + "right_line": 1429 + }, + { + "left_line": -1, + "right_line": 1430 + }, + { + "left_line": -1, + "right_line": 1431 + }, + { + "left_line": -1, + "right_line": 1432 + }, + { + "left_line": -1, + "right_line": 1433 + }, + { + "left_line": -1, + "right_line": 1434 + }, + { + "left_line": -1, + "right_line": 1435 + }, + { + "left_line": -1, + "right_line": 1436 + }, + { + "left_line": -1, + "right_line": 1437 + }, + { + "left_line": -1, + "right_line": 1438 + }, + { + "left_line": -1, + "right_line": 1439 + }, + { + "left_line": -1, + "right_line": 1440 + }, + { + "left_line": -1, + "right_line": 1441 + }, + { + "left_line": -1, + "right_line": 1442 + }, + { + "left_line": -1, + "right_line": 1443 + }, + { + "left_line": -1, + "right_line": 1444 + }, + { + "left_line": -1, + "right_line": 1445 + }, + { + "left_line": -1, + "right_line": 1446 + }, + { + "left_line": -1, + "right_line": 1447 + }, + { + "left_line": -1, + "right_line": 1448 + }, + { + "left_line": -1, + "right_line": 1449 + }, + { + "left_line": -1, + "right_line": 1450 + }, + { + "left_line": -1, + "right_line": 1451 + }, + { + "left_line": -1, + "right_line": 1452 + }, + { + "left_line": -1, + "right_line": 1453 + }, + { + "left_line": -1, + "right_line": 1454 + }, + { + "left_line": -1, + "right_line": 1455 + }, + { + "left_line": -1, + "right_line": 1456 + }, + { + "left_line": -1, + "right_line": 1457 + }, + { + "left_line": -1, + "right_line": 1458 + }, + { + "left_line": -1, + "right_line": 1459 + }, + { + "left_line": -1, + "right_line": 1460 + }, + { + "left_line": -1, + "right_line": 1461 + }, + { + "left_line": -1, + "right_line": 1462 + }, + { + "left_line": -1, + "right_line": 1463 + }, + { + "left_line": -1, + "right_line": 1464 + }, + { + "left_line": -1, + "right_line": 1465 + }, + { + "left_line": -1, + "right_line": 1466 + }, + { + "left_line": -1, + "right_line": 1467 + }, + { + "left_line": -1, + "right_line": 1468 + }, + { + "left_line": -1, + "right_line": 1469 + }, + { + "left_line": -1, + "right_line": 1470 + }, + { + "left_line": -1, + "right_line": 1471 + }, + { + "left_line": -1, + "right_line": 1472 + }, + { + "left_line": -1, + "right_line": 1473 + }, + { + "left_line": -1, + "right_line": 1474 + }, + { + "left_line": -1, + "right_line": 1475 + }, + { + "left_line": -1, + "right_line": 1476 + }, + { + "left_line": -1, + "right_line": 1477 + }, + { + "left_line": -1, + "right_line": 1478 + }, + { + "left_line": -1, + "right_line": 1479 + }, + { + "left_line": -1, + "right_line": 1480 + }, + { + "left_line": -1, + "right_line": 1481 + }, + { + "left_line": -1, + "right_line": 1482 + }, + { + "left_line": -1, + "right_line": 1483 + }, + { + "left_line": -1, + "right_line": 1484 + }, + { + "left_line": -1, + "right_line": 1485 + }, + { + "left_line": -1, + "right_line": 1486 + }, + { + "left_line": -1, + "right_line": 1487 + }, + { + "left_line": -1, + "right_line": 1488 + }, + { + "left_line": -1, + "right_line": 1489 + }, + { + "left_line": -1, + "right_line": 1490 + }, + { + "left_line": -1, + "right_line": 1491 + }, + { + "left_line": -1, + "right_line": 1492 + }, + { + "left_line": -1, + "right_line": 1493 + }, + { + "left_line": -1, + "right_line": 1494 + }, + { + "left_line": -1, + "right_line": 1495 + }, + { + "left_line": -1, + "right_line": 1496 + }, + { + "left_line": -1, + "right_line": 1497 + }, + { + "left_line": -1, + "right_line": 1498 + }, + { + "left_line": -1, + "right_line": 1499 + }, + { + "left_line": -1, + "right_line": 1500 + }, + { + "left_line": -1, + "right_line": 1501 + }, + { + "left_line": -1, + "right_line": 1502 + }, + { + "left_line": -1, + "right_line": 1503 + }, + { + "left_line": -1, + "right_line": 1504 + }, + { + "left_line": -1, + "right_line": 1505 + }, + { + "left_line": -1, + "right_line": 1506 + }, + { + "left_line": -1, + "right_line": 1507 + }, + { + "left_line": -1, + "right_line": 1508 + }, + { + "left_line": -1, + "right_line": 1509 + }, + { + "left_line": -1, + "right_line": 1510 + }, + { + "left_line": -1, + "right_line": 1511 + }, + { + "left_line": -1, + "right_line": 1512 + }, + { + "left_line": -1, + "right_line": 1513 + }, + { + "left_line": -1, + "right_line": 1514 + }, + { + "left_line": -1, + "right_line": 1515 + }, + { + "left_line": -1, + "right_line": 1516 + }, + { + "left_line": -1, + "right_line": 1517 + }, + { + "left_line": -1, + "right_line": 1518 + }, + { + "left_line": -1, + "right_line": 1519 + }, + { + "left_line": -1, + "right_line": 1520 + }, + { + "left_line": -1, + "right_line": 1521 + }, + { + "left_line": -1, + "right_line": 1522 + }, + { + "left_line": -1, + "right_line": 1523 + }, + { + "left_line": -1, + "right_line": 1524 + }, + { + "left_line": -1, + "right_line": 1525 + }, + { + "left_line": -1, + "right_line": 1526 + }, + { + "left_line": -1, + "right_line": 1527 + }, + { + "left_line": -1, + "right_line": 1528 + }, + { + "left_line": -1, + "right_line": 1529 + }, + { + "left_line": -1, + "right_line": 1530 + }, + { + "left_line": -1, + "right_line": 1531 + }, + { + "left_line": -1, + "right_line": 1532 + }, + { + "left_line": -1, + "right_line": 1533 + }, + { + "left_line": -1, + "right_line": 1534 + }, + { + "left_line": -1, + "right_line": 1535 + }, + { + "left_line": -1, + "right_line": 1536 + }, + { + "left_line": -1, + "right_line": 1537 + }, + { + "left_line": -1, + "right_line": 1538 + }, + { + "left_line": -1, + "right_line": 1539 + }, + { + "left_line": -1, + "right_line": 1540 + }, + { + "left_line": -1, + "right_line": 1541 + }, + { + "left_line": -1, + "right_line": 1542 + }, + { + "left_line": -1, + "right_line": 1543 + }, + { + "left_line": -1, + "right_line": 1544 + }, + { + "left_line": -1, + "right_line": 1545 + }, + { + "left_line": -1, + "right_line": 1546 + }, + { + "left_line": -1, + "right_line": 1547 + }, + { + "left_line": -1, + "right_line": 1548 + }, + { + "left_line": -1, + "right_line": 1549 + }, + { + "left_line": -1, + "right_line": 1550 + }, + { + "left_line": -1, + "right_line": 1551 + }, + { + "left_line": -1, + "right_line": 1552 + }, + { + "left_line": -1, + "right_line": 1553 + }, + { + "left_line": -1, + "right_line": 1554 + }, + { + "left_line": -1, + "right_line": 1555 + }, + { + "left_line": -1, + "right_line": 1556 + }, + { + "left_line": -1, + "right_line": 1557 + }, + { + "left_line": -1, + "right_line": 1558 + }, + { + "left_line": -1, + "right_line": 1559 + }, + { + "left_line": -1, + "right_line": 1560 + }, + { + "left_line": -1, + "right_line": 1561 + }, + { + "left_line": -1, + "right_line": 1562 + }, + { + "left_line": -1, + "right_line": 1563 + }, + { + "left_line": -1, + "right_line": 1564 + }, + { + "left_line": -1, + "right_line": 1565 + }, + { + "left_line": -1, + "right_line": 1566 + }, + { + "left_line": -1, + "right_line": 1567 + }, + { + "left_line": -1, + "right_line": 1568 + }, + { + "left_line": -1, + "right_line": 1569 + }, + { + "left_line": -1, + "right_line": 1570 + }, + { + "left_line": -1, + "right_line": 1571 + }, + { + "left_line": -1, + "right_line": 1572 + }, + { + "left_line": -1, + "right_line": 1573 + }, + { + "left_line": -1, + "right_line": 1574 + }, + { + "left_line": -1, + "right_line": 1575 + }, + { + "left_line": -1, + "right_line": 1576 + }, + { + "left_line": -1, + "right_line": 1577 + }, + { + "left_line": -1, + "right_line": 1578 + }, + { + "left_line": -1, + "right_line": 1579 + }, + { + "left_line": -1, + "right_line": 1580 + }, + { + "left_line": -1, + "right_line": 1581 + }, + { + "left_line": -1, + "right_line": 1582 + }, + { + "left_line": -1, + "right_line": 1583 + }, + { + "left_line": -1, + "right_line": 1584 + }, + { + "left_line": -1, + "right_line": 1585 + }, + { + "left_line": -1, + "right_line": 1586 + }, + { + "left_line": -1, + "right_line": 1587 + }, + { + "left_line": -1, + "right_line": 1588 + }, + { + "left_line": -1, + "right_line": 1589 + }, + { + "left_line": -1, + "right_line": 1590 + }, + { + "left_line": -1, + "right_line": 1591 + }, + { + "left_line": -1, + "right_line": 1592 + }, + { + "left_line": -1, + "right_line": 1593 + }, + { + "left_line": -1, + "right_line": 1594 + }, + { + "left_line": -1, + "right_line": 1595 + }, + { + "left_line": -1, + "right_line": 1596 + }, + { + "left_line": -1, + "right_line": 1597 + }, + { + "left_line": -1, + "right_line": 1598 + }, + { + "left_line": -1, + "right_line": 1599 + }, + { + "left_line": -1, + "right_line": 1600 + }, + { + "left_line": -1, + "right_line": 1601 + }, + { + "left_line": -1, + "right_line": 1602 + }, + { + "left_line": -1, + "right_line": 1603 + }, + { + "left_line": -1, + "right_line": 1604 + }, + { + "left_line": -1, + "right_line": 1605 + }, + { + "left_line": -1, + "right_line": 1606 + }, + { + "left_line": -1, + "right_line": 1607 + }, + { + "left_line": -1, + "right_line": 1608 + }, + { + "left_line": -1, + "right_line": 1609 + }, + { + "left_line": -1, + "right_line": 1610 + }, + { + "left_line": -1, + "right_line": 1611 + }, + { + "left_line": -1, + "right_line": 1612 + }, + { + "left_line": -1, + "right_line": 1613 + }, + { + "left_line": -1, + "right_line": 1614 + }, + { + "left_line": -1, + "right_line": 1615 + }, + { + "left_line": -1, + "right_line": 1616 + }, + { + "left_line": -1, + "right_line": 1617 + }, + { + "left_line": -1, + "right_line": 1618 + }, + { + "left_line": -1, + "right_line": 1619 + }, + { + "left_line": -1, + "right_line": 1620 + }, + { + "left_line": -1, + "right_line": 1621 + }, + { + "left_line": -1, + "right_line": 1622 + }, + { + "left_line": -1, + "right_line": 1623 + }, + { + "left_line": -1, + "right_line": 1624 + }, + { + "left_line": -1, + "right_line": 1625 + }, + { + "left_line": -1, + "right_line": 1626 + }, + { + "left_line": -1, + "right_line": 1627 + }, + { + "left_line": -1, + "right_line": 1628 + }, + { + "left_line": -1, + "right_line": 1629 + }, + { + "left_line": -1, + "right_line": 1630 + }, + { + "left_line": -1, + "right_line": 1631 + }, + { + "left_line": -1, + "right_line": 1632 + }, + { + "left_line": -1, + "right_line": 1633 + }, + { + "left_line": -1, + "right_line": 1634 + }, + { + "left_line": -1, + "right_line": 1635 + }, + { + "left_line": -1, + "right_line": 1636 + }, + { + "left_line": -1, + "right_line": 1637 + }, + { + "left_line": -1, + "right_line": 1638 + }, + { + "left_line": -1, + "right_line": 1639 + }, + { + "left_line": -1, + "right_line": 1640 + }, + { + "left_line": -1, + "right_line": 1641 + }, + { + "left_line": -1, + "right_line": 1642 + }, + { + "left_line": -1, + "right_line": 1643 + }, + { + "left_line": -1, + "right_line": 1644 + }, + { + "left_line": -1, + "right_line": 1645 + }, + { + "left_line": -1, + "right_line": 1646 + }, + { + "left_line": -1, + "right_line": 1647 + }, + { + "left_line": -1, + "right_line": 1648 + }, + { + "left_line": -1, + "right_line": 1649 + }, + { + "left_line": -1, + "right_line": 1650 + }, + { + "left_line": -1, + "right_line": 1651 + }, + { + "left_line": -1, + "right_line": 1652 + }, + { + "left_line": -1, + "right_line": 1653 + }, + { + "left_line": -1, + "right_line": 1654 + }, + { + "left_line": -1, + "right_line": 1655 + }, + { + "left_line": -1, + "right_line": 1656 + }, + { + "left_line": -1, + "right_line": 1657 + }, + { + "left_line": -1, + "right_line": 1658 + }, + { + "left_line": -1, + "right_line": 1659 + }, + { + "left_line": -1, + "right_line": 1660 + }, + { + "left_line": -1, + "right_line": 1661 + }, + { + "left_line": -1, + "right_line": 1662 + }, + { + "left_line": -1, + "right_line": 1663 + }, + { + "left_line": -1, + "right_line": 1664 + }, + { + "left_line": -1, + "right_line": 1665 + }, + { + "left_line": -1, + "right_line": 1666 + }, + { + "left_line": -1, + "right_line": 1667 + }, + { + "left_line": -1, + "right_line": 1668 + }, + { + "left_line": -1, + "right_line": 1669 + }, + { + "left_line": -1, + "right_line": 1670 + }, + { + "left_line": -1, + "right_line": 1671 + }, + { + "left_line": -1, + "right_line": 1672 + }, + { + "left_line": -1, + "right_line": 1673 + }, + { + "left_line": -1, + "right_line": 1674 + }, + { + "left_line": -1, + "right_line": 1675 + }, + { + "left_line": -1, + "right_line": 1676 + }, + { + "left_line": -1, + "right_line": 1677 + }, + { + "left_line": -1, + "right_line": 1678 + }, + { + "left_line": -1, + "right_line": 1679 + }, + { + "left_line": -1, + "right_line": 1680 + }, + { + "left_line": -1, + "right_line": 1681 + }, + { + "left_line": -1, + "right_line": 1682 + }, + { + "left_line": -1, + "right_line": 1683 + }, + { + "left_line": -1, + "right_line": 1684 + }, + { + "left_line": -1, + "right_line": 1685 + }, + { + "left_line": -1, + "right_line": 1686 + }, + { + "left_line": -1, + "right_line": 1687 + }, + { + "left_line": -1, + "right_line": 1688 + }, + { + "left_line": -1, + "right_line": 1689 + }, + { + "left_line": -1, + "right_line": 1690 + }, + { + "left_line": -1, + "right_line": 1691 + }, + { + "left_line": -1, + "right_line": 1692 + }, + { + "left_line": -1, + "right_line": 1693 + }, + { + "left_line": -1, + "right_line": 1694 + }, + { + "left_line": -1, + "right_line": 1695 + }, + { + "left_line": -1, + "right_line": 1696 + }, + { + "left_line": -1, + "right_line": 1697 + }, + { + "left_line": -1, + "right_line": 1698 + }, + { + "left_line": -1, + "right_line": 1699 + }, + { + "left_line": -1, + "right_line": 1700 + }, + { + "left_line": -1, + "right_line": 1701 + }, + { + "left_line": -1, + "right_line": 1702 + }, + { + "left_line": -1, + "right_line": 1703 + }, + { + "left_line": -1, + "right_line": 1704 + }, + { + "left_line": -1, + "right_line": 1705 + }, + { + "left_line": -1, + "right_line": 1706 + }, + { + "left_line": -1, + "right_line": 1707 + }, + { + "left_line": -1, + "right_line": 1708 + }, + { + "left_line": -1, + "right_line": 1709 + }, + { + "left_line": -1, + "right_line": 1710 + }, + { + "left_line": -1, + "right_line": 1711 + }, + { + "left_line": -1, + "right_line": 1712 + }, + { + "left_line": -1, + "right_line": 1713 + }, + { + "left_line": -1, + "right_line": 1714 + }, + { + "left_line": -1, + "right_line": 1715 + }, + { + "left_line": -1, + "right_line": 1716 + }, + { + "left_line": -1, + "right_line": 1717 + }, + { + "left_line": -1, + "right_line": 1718 + }, + { + "left_line": -1, + "right_line": 1719 + }, + { + "left_line": -1, + "right_line": 1720 + }, + { + "left_line": -1, + "right_line": 1721 + }, + { + "left_line": -1, + "right_line": 1722 + }, + { + "left_line": -1, + "right_line": 1723 + }, + { + "left_line": -1, + "right_line": 1724 + }, + { + "left_line": -1, + "right_line": 1725 + }, + { + "left_line": -1, + "right_line": 1726 + }, + { + "left_line": -1, + "right_line": 1727 + }, + { + "left_line": -1, + "right_line": 1728 + }, + { + "left_line": -1, + "right_line": 1729 + }, + { + "left_line": -1, + "right_line": 1730 + }, + { + "left_line": -1, + "right_line": 1731 + }, + { + "left_line": -1, + "right_line": 1732 + }, + { + "left_line": -1, + "right_line": 1733 + }, + { + "left_line": -1, + "right_line": 1734 + }, + { + "left_line": -1, + "right_line": 1735 + }, + { + "left_line": -1, + "right_line": 1736 + }, + { + "left_line": -1, + "right_line": 1737 + }, + { + "left_line": -1, + "right_line": 1738 + }, + { + "left_line": -1, + "right_line": 1739 + }, + { + "left_line": -1, + "right_line": 1740 + }, + { + "left_line": -1, + "right_line": 1741 + }, + { + "left_line": -1, + "right_line": 1742 + }, + { + "left_line": -1, + "right_line": 1743 + }, + { + "left_line": -1, + "right_line": 1744 + }, + { + "left_line": -1, + "right_line": 1745 + }, + { + "left_line": -1, + "right_line": 1746 + }, + { + "left_line": -1, + "right_line": 1747 + }, + { + "left_line": -1, + "right_line": 1748 + }, + { + "left_line": -1, + "right_line": 1749 + }, + { + "left_line": -1, + "right_line": 1750 + }, + { + "left_line": -1, + "right_line": 1751 + }, + { + "left_line": -1, + "right_line": 1752 + }, + { + "left_line": -1, + "right_line": 1753 + }, + { + "left_line": -1, + "right_line": 1754 + }, + { + "left_line": -1, + "right_line": 1755 + }, + { + "left_line": -1, + "right_line": 1756 + }, + { + "left_line": -1, + "right_line": 1757 + }, + { + "left_line": -1, + "right_line": 1758 + }, + { + "left_line": -1, + "right_line": 1759 + }, + { + "left_line": -1, + "right_line": 1760 + }, + { + "left_line": -1, + "right_line": 1761 + }, + { + "left_line": -1, + "right_line": 1762 + }, + { + "left_line": -1, + "right_line": 1763 + }, + { + "left_line": -1, + "right_line": 1764 + }, + { + "left_line": -1, + "right_line": 1765 + }, + { + "left_line": -1, + "right_line": 1766 + }, + { + "left_line": -1, + "right_line": 1767 + }, + { + "left_line": -1, + "right_line": 1768 + }, + { + "left_line": -1, + "right_line": 1769 + }, + { + "left_line": -1, + "right_line": 1770 + }, + { + "left_line": -1, + "right_line": 1771 + }, + { + "left_line": -1, + "right_line": 1772 + }, + { + "left_line": -1, + "right_line": 1773 + }, + { + "left_line": -1, + "right_line": 1774 + }, + { + "left_line": -1, + "right_line": 1775 + }, + { + "left_line": -1, + "right_line": 1776 + }, + { + "left_line": -1, + "right_line": 1777 + }, + { + "left_line": -1, + "right_line": 1778 + }, + { + "left_line": -1, + "right_line": 1779 + }, + { + "left_line": -1, + "right_line": 1780 + }, + { + "left_line": -1, + "right_line": 1781 + }, + { + "left_line": -1, + "right_line": 1782 + }, + { + "left_line": -1, + "right_line": 1783 + }, + { + "left_line": -1, + "right_line": 1784 + }, + { + "left_line": -1, + "right_line": 1785 + }, + { + "left_line": -1, + "right_line": 1786 + }, + { + "left_line": -1, + "right_line": 1787 + }, + { + "left_line": -1, + "right_line": 1788 + }, + { + "left_line": -1, + "right_line": 1789 + }, + { + "left_line": -1, + "right_line": 1790 + }, + { + "left_line": -1, + "right_line": 1791 + }, + { + "left_line": -1, + "right_line": 1792 + }, + { + "left_line": -1, + "right_line": 1793 + }, + { + "left_line": -1, + "right_line": 1794 + }, + { + "left_line": -1, + "right_line": 1795 + }, + { + "left_line": -1, + "right_line": 1796 + }, + { + "left_line": -1, + "right_line": 1797 + }, + { + "left_line": -1, + "right_line": 1798 + }, + { + "left_line": -1, + "right_line": 1799 + }, + { + "left_line": -1, + "right_line": 1800 + }, + { + "left_line": -1, + "right_line": 1801 + }, + { + "left_line": -1, + "right_line": 1802 + }, + { + "left_line": -1, + "right_line": 1803 + }, + { + "left_line": -1, + "right_line": 1804 + }, + { + "left_line": -1, + "right_line": 1805 + }, + { + "left_line": -1, + "right_line": 1806 + }, + { + "left_line": -1, + "right_line": 1807 + }, + { + "left_line": -1, + "right_line": 1808 + }, + { + "left_line": -1, + "right_line": 1809 + }, + { + "left_line": -1, + "right_line": 1810 + }, + { + "left_line": -1, + "right_line": 1811 + }, + { + "left_line": -1, + "right_line": 1812 + }, + { + "left_line": -1, + "right_line": 1813 + }, + { + "left_line": -1, + "right_line": 1814 + }, + { + "left_line": -1, + "right_line": 1815 + }, + { + "left_line": -1, + "right_line": 1816 + }, + { + "left_line": -1, + "right_line": 1817 + }, + { + "left_line": -1, + "right_line": 1818 + }, + { + "left_line": -1, + "right_line": 1819 + }, + { + "left_line": -1, + "right_line": 1820 + }, + { + "left_line": -1, + "right_line": 1821 + }, + { + "left_line": -1, + "right_line": 1822 + }, + { + "left_line": -1, + "right_line": 1823 + }, + { + "left_line": -1, + "right_line": 1824 + }, + { + "left_line": -1, + "right_line": 1825 + }, + { + "left_line": -1, + "right_line": 1826 + }, + { + "left_line": -1, + "right_line": 1827 + }, + { + "left_line": -1, + "right_line": 1828 + }, + { + "left_line": -1, + "right_line": 1829 + }, + { + "left_line": -1, + "right_line": 1830 + }, + { + "left_line": -1, + "right_line": 1831 + }, + { + "left_line": -1, + "right_line": 1832 + }, + { + "left_line": -1, + "right_line": 1833 + }, + { + "left_line": -1, + "right_line": 1834 + }, + { + "left_line": -1, + "right_line": 1835 + }, + { + "left_line": -1, + "right_line": 1836 + }, + { + "left_line": -1, + "right_line": 1837 + }, + { + "left_line": -1, + "right_line": 1838 + }, + { + "left_line": -1, + "right_line": 1839 + }, + { + "left_line": -1, + "right_line": 1840 + }, + { + "left_line": -1, + "right_line": 1841 + }, + { + "left_line": -1, + "right_line": 1842 + }, + { + "left_line": -1, + "right_line": 1843 + }, + { + "left_line": -1, + "right_line": 1844 + }, + { + "left_line": -1, + "right_line": 1845 + }, + { + "left_line": -1, + "right_line": 1846 + }, + { + "left_line": -1, + "right_line": 1847 + }, + { + "left_line": -1, + "right_line": 1848 + }, + { + "left_line": -1, + "right_line": 1849 + }, + { + "left_line": -1, + "right_line": 1850 + }, + { + "left_line": -1, + "right_line": 1851 + }, + { + "left_line": -1, + "right_line": 1852 + }, + { + "left_line": -1, + "right_line": 1853 + }, + { + "left_line": -1, + "right_line": 1854 + }, + { + "left_line": -1, + "right_line": 1855 + }, + { + "left_line": -1, + "right_line": 1856 + }, + { + "left_line": -1, + "right_line": 1857 + }, + { + "left_line": -1, + "right_line": 1858 + }, + { + "left_line": -1, + "right_line": 1859 + }, + { + "left_line": -1, + "right_line": 1860 + }, + { + "left_line": -1, + "right_line": 1861 + }, + { + "left_line": -1, + "right_line": 1862 + }, + { + "left_line": -1, + "right_line": 1863 + }, + { + "left_line": -1, + "right_line": 1864 + }, + { + "left_line": -1, + "right_line": 1865 + }, + { + "left_line": -1, + "right_line": 1866 + }, + { + "left_line": -1, + "right_line": 1867 + }, + { + "left_line": -1, + "right_line": 1868 + }, + { + "left_line": -1, + "right_line": 1869 + }, + { + "left_line": -1, + "right_line": 1870 + }, + { + "left_line": -1, + "right_line": 1871 + }, + { + "left_line": -1, + "right_line": 1872 + }, + { + "left_line": -1, + "right_line": 1873 + }, + { + "left_line": -1, + "right_line": 1874 + }, + { + "left_line": -1, + "right_line": 1875 + }, + { + "left_line": -1, + "right_line": 1876 + }, + { + "left_line": -1, + "right_line": 1877 + }, + { + "left_line": -1, + "right_line": 1878 + }, + { + "left_line": -1, + "right_line": 1879 + }, + { + "left_line": -1, + "right_line": 1880 + }, + { + "left_line": -1, + "right_line": 1881 + }, + { + "left_line": -1, + "right_line": 1882 + }, + { + "left_line": -1, + "right_line": 1883 + }, + { + "left_line": -1, + "right_line": 1884 + }, + { + "left_line": -1, + "right_line": 1885 + }, + { + "left_line": -1, + "right_line": 1886 + }, + { + "left_line": -1, + "right_line": 1887 + }, + { + "left_line": -1, + "right_line": 1888 + }, + { + "left_line": -1, + "right_line": 1889 + }, + { + "left_line": -1, + "right_line": 1890 + }, + { + "left_line": -1, + "right_line": 1891 + }, + { + "left_line": -1, + "right_line": 1892 + }, + { + "left_line": -1, + "right_line": 1893 + }, + { + "left_line": -1, + "right_line": 1894 + }, + { + "left_line": -1, + "right_line": 1895 + }, + { + "left_line": -1, + "right_line": 1896 + }, + { + "left_line": -1, + "right_line": 1897 + }, + { + "left_line": -1, + "right_line": 1898 + }, + { + "left_line": -1, + "right_line": 1899 + }, + { + "left_line": -1, + "right_line": 1900 + }, + { + "left_line": -1, + "right_line": 1901 + }, + { + "left_line": -1, + "right_line": 1902 + }, + { + "left_line": -1, + "right_line": 1903 + }, + { + "left_line": -1, + "right_line": 1904 + }, + { + "left_line": -1, + "right_line": 1905 + }, + { + "left_line": -1, + "right_line": 1906 + }, + { + "left_line": -1, + "right_line": 1907 + }, + { + "left_line": -1, + "right_line": 1908 + }, + { + "left_line": -1, + "right_line": 1909 + }, + { + "left_line": -1, + "right_line": 1910 + }, + { + "left_line": -1, + "right_line": 1911 + }, + { + "left_line": -1, + "right_line": 1912 + }, + { + "left_line": -1, + "right_line": 1913 + }, + { + "left_line": -1, + "right_line": 1914 + }, + { + "left_line": -1, + "right_line": 1915 + }, + { + "left_line": -1, + "right_line": 1916 + }, + { + "left_line": -1, + "right_line": 1917 + }, + { + "left_line": -1, + "right_line": 1918 + }, + { + "left_line": -1, + "right_line": 1919 + }, + { + "left_line": -1, + "right_line": 1920 + }, + { + "left_line": -1, + "right_line": 1921 + }, + { + "left_line": -1, + "right_line": 1922 + }, + { + "left_line": -1, + "right_line": 1923 + }, + { + "left_line": -1, + "right_line": 1924 + }, + { + "left_line": -1, + "right_line": 1925 + }, + { + "left_line": -1, + "right_line": 1926 + }, + { + "left_line": -1, + "right_line": 1927 + }, + { + "left_line": -1, + "right_line": 1928 + }, + { + "left_line": -1, + "right_line": 1929 + }, + { + "left_line": -1, + "right_line": 1930 + }, + { + "left_line": -1, + "right_line": 1931 + }, + { + "left_line": -1, + "right_line": 1932 + }, + { + "left_line": -1, + "right_line": 1933 + }, + { + "left_line": -1, + "right_line": 1934 + }, + { + "left_line": -1, + "right_line": 1935 + }, + { + "left_line": -1, + "right_line": 1936 + }, + { + "left_line": -1, + "right_line": 1937 + }, + { + "left_line": -1, + "right_line": 1938 + }, + { + "left_line": -1, + "right_line": 1939 + }, + { + "left_line": -1, + "right_line": 1940 + }, + { + "left_line": -1, + "right_line": 1941 + }, + { + "left_line": -1, + "right_line": 1942 + }, + { + "left_line": -1, + "right_line": 1943 + }, + { + "left_line": -1, + "right_line": 1944 + }, + { + "left_line": -1, + "right_line": 1945 + }, + { + "left_line": -1, + "right_line": 1946 + }, + { + "left_line": -1, + "right_line": 1947 + }, + { + "left_line": -1, + "right_line": 1948 + }, + { + "left_line": -1, + "right_line": 1949 + }, + { + "left_line": -1, + "right_line": 1950 + }, + { + "left_line": -1, + "right_line": 1951 + }, + { + "left_line": -1, + "right_line": 1952 + }, + { + "left_line": -1, + "right_line": 1953 + }, + { + "left_line": -1, + "right_line": 1954 + }, + { + "left_line": -1, + "right_line": 1955 + }, + { + "left_line": -1, + "right_line": 1956 + }, + { + "left_line": -1, + "right_line": 1957 + }, + { + "left_line": -1, + "right_line": 1958 + }, + { + "left_line": -1, + "right_line": 1959 + }, + { + "left_line": -1, + "right_line": 1960 + }, + { + "left_line": -1, + "right_line": 1961 + }, + { + "left_line": -1, + "right_line": 1962 + }, + { + "left_line": -1, + "right_line": 1963 + }, + { + "left_line": -1, + "right_line": 1964 + }, + { + "left_line": -1, + "right_line": 1965 + }, + { + "left_line": -1, + "right_line": 1966 + }, + { + "left_line": -1, + "right_line": 1967 + }, + { + "left_line": -1, + "right_line": 1968 + }, + { + "left_line": -1, + "right_line": 1969 + }, + { + "left_line": -1, + "right_line": 1970 + }, + { + "left_line": -1, + "right_line": 1971 + }, + { + "left_line": -1, + "right_line": 1972 + }, + { + "left_line": -1, + "right_line": 1973 + }, + { + "left_line": -1, + "right_line": 1974 + }, + { + "left_line": -1, + "right_line": 1975 + }, + { + "left_line": -1, + "right_line": 1976 + }, + { + "left_line": -1, + "right_line": 1977 + }, + { + "left_line": -1, + "right_line": 1978 + }, + { + "left_line": -1, + "right_line": 1979 + }, + { + "left_line": -1, + "right_line": 1980 + }, + { + "left_line": -1, + "right_line": 1981 + }, + { + "left_line": -1, + "right_line": 1982 + }, + { + "left_line": -1, + "right_line": 1983 + }, + { + "left_line": -1, + "right_line": 1984 + }, + { + "left_line": -1, + "right_line": 1985 + }, + { + "left_line": -1, + "right_line": 1986 + }, + { + "left_line": -1, + "right_line": 1987 + }, + { + "left_line": -1, + "right_line": 1988 + }, + { + "left_line": -1, + "right_line": 1989 + }, + { + "left_line": -1, + "right_line": 1990 + }, + { + "left_line": -1, + "right_line": 1991 + }, + { + "left_line": -1, + "right_line": 1992 + }, + { + "left_line": -1, + "right_line": 1993 + }, + { + "left_line": -1, + "right_line": 1994 + }, + { + "left_line": -1, + "right_line": 1995 + }, + { + "left_line": -1, + "right_line": 1996 + }, + { + "left_line": -1, + "right_line": 1997 + }, + { + "left_line": -1, + "right_line": 1998 + }, + { + "left_line": -1, + "right_line": 1999 + }, + { + "left_line": -1, + "right_line": 2000 + }, + { + "left_line": -1, + "right_line": 2001 + }, + { + "left_line": -1, + "right_line": 2002 + }, + { + "left_line": -1, + "right_line": 2003 + }, + { + "left_line": -1, + "right_line": 2004 + }, + { + "left_line": -1, + "right_line": 2005 + }, + { + "left_line": -1, + "right_line": 2006 + }, + { + "left_line": -1, + "right_line": 2007 + }, + { + "left_line": -1, + "right_line": 2008 + }, + { + "left_line": -1, + "right_line": 2009 + }, + { + "left_line": -1, + "right_line": 2010 + }, + { + "left_line": -1, + "right_line": 2011 + }, + { + "left_line": -1, + "right_line": 2012 + }, + { + "left_line": -1, + "right_line": 2013 + }, + { + "left_line": -1, + "right_line": 2014 + }, + { + "left_line": -1, + "right_line": 2015 + }, + { + "left_line": -1, + "right_line": 2016 + }, + { + "left_line": -1, + "right_line": 2017 + }, + { + "left_line": -1, + "right_line": 2018 + }, + { + "left_line": -1, + "right_line": 2019 + }, + { + "left_line": -1, + "right_line": 2020 + }, + { + "left_line": -1, + "right_line": 2021 + }, + { + "left_line": -1, + "right_line": 2022 + }, + { + "left_line": -1, + "right_line": 2023 + }, + { + "left_line": -1, + "right_line": 2024 + }, + { + "left_line": -1, + "right_line": 2025 + }, + { + "left_line": -1, + "right_line": 2026 + }, + { + "left_line": -1, + "right_line": 2027 + }, + { + "left_line": -1, + "right_line": 2028 + }, + { + "left_line": -1, + "right_line": 2029 + }, + { + "left_line": -1, + "right_line": 2030 + }, + { + "left_line": -1, + "right_line": 2031 + }, + { + "left_line": -1, + "right_line": 2032 + }, + { + "left_line": -1, + "right_line": 2033 + }, + { + "left_line": -1, + "right_line": 2034 + }, + { + "left_line": -1, + "right_line": 2035 + }, + { + "left_line": -1, + "right_line": 2036 + }, + { + "left_line": -1, + "right_line": 2037 + }, + { + "left_line": -1, + "right_line": 2038 + }, + { + "left_line": -1, + "right_line": 2039 + }, + { + "left_line": -1, + "right_line": 2040 + }, + { + "left_line": -1, + "right_line": 2041 + }, + { + "left_line": -1, + "right_line": 2042 + }, + { + "left_line": -1, + "right_line": 2043 + }, + { + "left_line": -1, + "right_line": 2044 + }, + { + "left_line": -1, + "right_line": 2045 + }, + { + "left_line": -1, + "right_line": 2046 + }, + { + "left_line": -1, + "right_line": 2047 + }, + { + "left_line": -1, + "right_line": 2048 + }, + { + "left_line": -1, + "right_line": 2049 + }, + { + "left_line": -1, + "right_line": 2050 + }, + { + "left_line": -1, + "right_line": 2051 + }, + { + "left_line": -1, + "right_line": 2052 + }, + { + "left_line": -1, + "right_line": 2053 + }, + { + "left_line": -1, + "right_line": 2054 + }, + { + "left_line": -1, + "right_line": 2055 + }, + { + "left_line": -1, + "right_line": 2056 + }, + { + "left_line": -1, + "right_line": 2057 + }, + { + "left_line": -1, + "right_line": 2058 + }, + { + "left_line": -1, + "right_line": 2059 + }, + { + "left_line": -1, + "right_line": 2060 + }, + { + "left_line": -1, + "right_line": 2061 + }, + { + "left_line": -1, + "right_line": 2062 + }, + { + "left_line": -1, + "right_line": 2063 + }, + { + "left_line": -1, + "right_line": 2064 + }, + { + "left_line": -1, + "right_line": 2065 + }, + { + "left_line": -1, + "right_line": 2066 + }, + { + "left_line": -1, + "right_line": 2067 + }, + { + "left_line": -1, + "right_line": 2068 + }, + { + "left_line": -1, + "right_line": 2069 + }, + { + "left_line": -1, + "right_line": 2070 + }, + { + "left_line": -1, + "right_line": 2071 + }, + { + "left_line": -1, + "right_line": 2072 + }, + { + "left_line": -1, + "right_line": 2073 + }, + { + "left_line": -1, + "right_line": 2074 + }, + { + "left_line": -1, + "right_line": 2075 + }, + { + "left_line": -1, + "right_line": 2076 + }, + { + "left_line": -1, + "right_line": 2077 + }, + { + "left_line": -1, + "right_line": 2078 + }, + { + "left_line": -1, + "right_line": 2079 + }, + { + "left_line": -1, + "right_line": 2080 + }, + { + "left_line": -1, + "right_line": 2081 + }, + { + "left_line": -1, + "right_line": 2082 + }, + { + "left_line": -1, + "right_line": 2083 + }, + { + "left_line": -1, + "right_line": 2084 + }, + { + "left_line": -1, + "right_line": 2085 + }, + { + "left_line": -1, + "right_line": 2086 + }, + { + "left_line": -1, + "right_line": 2087 + }, + { + "left_line": -1, + "right_line": 2088 + }, + { + "left_line": -1, + "right_line": 2089 + }, + { + "left_line": -1, + "right_line": 2090 + }, + { + "left_line": -1, + "right_line": 2091 + }, + { + "left_line": -1, + "right_line": 2092 + }, + { + "left_line": -1, + "right_line": 2093 + }, + { + "left_line": -1, + "right_line": 2094 + }, + { + "left_line": -1, + "right_line": 2095 + }, + { + "left_line": -1, + "right_line": 2096 + }, + { + "left_line": -1, + "right_line": 2097 + }, + { + "left_line": -1, + "right_line": 2098 + }, + { + "left_line": -1, + "right_line": 2099 + }, + { + "left_line": -1, + "right_line": 2100 + }, + { + "left_line": -1, + "right_line": 2101 + }, + { + "left_line": -1, + "right_line": 2102 + }, + { + "left_line": -1, + "right_line": 2103 + }, + { + "left_line": -1, + "right_line": 2104 + }, + { + "left_line": -1, + "right_line": 2105 + }, + { + "left_line": -1, + "right_line": 2106 + }, + { + "left_line": -1, + "right_line": 2107 + }, + { + "left_line": -1, + "right_line": 2108 + }, + { + "left_line": -1, + "right_line": 2109 + }, + { + "left_line": -1, + "right_line": 2110 + }, + { + "left_line": -1, + "right_line": 2111 + }, + { + "left_line": -1, + "right_line": 2112 + }, + { + "left_line": -1, + "right_line": 2113 + }, + { + "left_line": -1, + "right_line": 2114 + }, + { + "left_line": -1, + "right_line": 2115 + } + ] +} \ No newline at end of file diff --git a/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/metadata.json b/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/metadata.json new file mode 100644 index 0000000..ce4af56 --- /dev/null +++ b/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/metadata.json @@ -0,0 +1,12 @@ +{ + "repo": "https://github.com/sinatra/sinatra.git", + "parent": "f517a5da89bee6a79edf0338c03d8df7a01f2b8d", + "commit": "0721b2aa893be81cab423144ef16122b8400f398", + "message": "Use Exception#detailed_message to show backtrace (#1952)", + "author": "Yusuke Endoh", + "date": "2023-10-14T03:31:21+09:00", + "language": "ruby", + "files_changed": 1, + "insertions": 13, + "deletions": 1 +} diff --git a/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/new.rb b/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/new.rb new file mode 100644 index 0000000..b986d28 --- /dev/null +++ b/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/new.rb @@ -0,0 +1,2115 @@ +# frozen_string_literal: true + +# external dependencies +require 'rack' +require 'tilt' +require 'rack/protection' +require 'mustermann' +require 'mustermann/sinatra' +require 'mustermann/regular' + +# stdlib dependencies +require 'time' +require 'uri' + +# other files we need +require 'sinatra/indifferent_hash' +require 'sinatra/show_exceptions' +require 'sinatra/version' + +module Sinatra + # The request object. See Rack::Request for more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Request + class Request < Rack::Request + HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/.freeze + HEADER_VALUE_WITH_PARAMS = %r{(?:(?:\w+|\*)/(?:\w+(?:\.|-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*}.freeze + + # Returns an array of acceptable media types for the response + def accept + @env['sinatra.accept'] ||= if @env.include?('HTTP_ACCEPT') && (@env['HTTP_ACCEPT'].to_s != '') + @env['HTTP_ACCEPT'] + .to_s + .scan(HEADER_VALUE_WITH_PARAMS) + .map! { |e| AcceptEntry.new(e) } + .sort + else + [AcceptEntry.new('*/*')] + end + end + + def accept?(type) + preferred_type(type).to_s.include?(type) + end + + def preferred_type(*types) + return accept.first if types.empty? + + types.flatten! + return types.first if accept.empty? + + accept.detect do |accept_header| + type = types.detect { |t| MimeTypeEntry.new(t).accepts?(accept_header) } + return type if type + end + end + + alias secure? ssl? + + def forwarded? + @env.include? 'HTTP_X_FORWARDED_HOST' + end + + def safe? + get? || head? || options? || trace? + end + + def idempotent? + safe? || put? || delete? || link? || unlink? + end + + def link? + request_method == 'LINK' + end + + def unlink? + request_method == 'UNLINK' + end + + def params + super + rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e + raise BadRequest, "Invalid query parameters: #{Rack::Utils.escape_html(e.message)}" + rescue EOFError => e + raise BadRequest, "Invalid multipart/form-data: #{Rack::Utils.escape_html(e.message)}" + end + + class AcceptEntry + attr_accessor :params + attr_reader :entry + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @entry = entry + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + @q = @params.delete('q') { 1.0 }.to_f + end + + def <=>(other) + other.priority <=> priority + end + + def priority + # We sort in descending order; better matches should be higher. + [@q, -@type.count('*'), @params.size] + end + + def to_str + @type + end + + def to_s(full = false) + full ? entry : to_str + end + + def respond_to?(*args) + super || to_str.respond_to?(*args) + end + + def method_missing(*args, &block) + to_str.send(*args, &block) + end + end + + class MimeTypeEntry + attr_reader :params + + def initialize(entry) + params = entry.scan(HEADER_PARAM).map! do |s| + key, value = s.strip.split('=', 2) + value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"') + [key, value] + end + + @type = entry[/[^;]+/].delete(' ') + @params = params.to_h + end + + def accepts?(entry) + File.fnmatch(entry, self) && matches_params?(entry.params) + end + + def to_str + @type + end + + def matches_params?(params) + return true if @params.empty? + + params.all? { |k, v| !@params.key?(k) || @params[k] == v } + end + end + end + + # The response object. See Rack::Response and Rack::Response::Helpers for + # more info: + # https://rubydoc.info/github/rack/rack/main/Rack/Response + # https://rubydoc.info/github/rack/rack/main/Rack/Response/Helpers + class Response < Rack::Response + DROP_BODY_RESPONSES = [204, 304].freeze + + def body=(value) + value = value.body while Rack::Response === value + @body = String === value ? [value.to_str] : value + end + + def each + block_given? ? super : enum_for(:each) + end + + def finish + result = body + + if drop_content_info? + headers.delete 'Content-Length' + headers.delete 'Content-Type' + end + + if drop_body? + close + result = [] + end + + if calculate_content_length? + # if some other code has already set Content-Length, don't muck with it + # currently, this would be the static file-handler + headers['Content-Length'] = body.map(&:bytesize).reduce(0, :+).to_s + end + + [status, headers, result] + end + + private + + def calculate_content_length? + headers['Content-Type'] && !headers['Content-Length'] && (Array === body) + end + + def drop_content_info? + informational? || drop_body? + end + + def drop_body? + DROP_BODY_RESPONSES.include?(status) + end + end + + # Some Rack handlers (Rainbows!) implement an extended body object protocol, however, + # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question. + # This middleware will detect an extended body object and will make sure it reaches the + # handler directly. We do this here, so our middleware and middleware set up by the app will + # still be able to run. + class ExtendedRack < Struct.new(:app) + def call(env) + result = app.call(env) + callback = env['async.callback'] + return result unless callback && async?(*result) + + after_response { callback.call result } + setup_close(env, *result) + throw :async + end + + private + + def setup_close(env, _status, _headers, body) + return unless body.respond_to?(:close) && env.include?('async.close') + + env['async.close'].callback { body.close } + env['async.close'].errback { body.close } + end + + def after_response(&block) + raise NotImplementedError, 'only supports EventMachine at the moment' unless defined? EventMachine + + EventMachine.next_tick(&block) + end + + def async?(status, _headers, body) + return true if status == -1 + + body.respond_to?(:callback) && body.respond_to?(:errback) + end + end + + # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing, + # if another CommonLogger is already in the middleware chain. + class CommonLogger < Rack::CommonLogger + def call(env) + env['sinatra.commonlogger'] ? @app.call(env) : super + end + + superclass.class_eval do + alias_method :call_without_check, :call unless method_defined? :call_without_check + def call(env) + env['sinatra.commonlogger'] = true + call_without_check(env) + end + end + end + + class Error < StandardError # :nodoc: + end + + class BadRequest < Error # :nodoc: + def http_status; 400 end + end + + class NotFound < Error # :nodoc: + def http_status; 404 end + end + + # Methods available to routes, before/after filters, and views. + module Helpers + # Set or retrieve the response status code. + def status(value = nil) + response.status = Rack::Utils.status_code(value) if value + response.status + end + + # Set or retrieve the response body. When a block is given, + # evaluation is deferred until the body is read with #each. + def body(value = nil, &block) + if block_given? + def block.each; yield(call) end + response.body = block + elsif value + # Rack 2.0 returns a Rack::File::Iterator here instead of + # Rack::File as it was in the previous API. + unless request.head? || value.is_a?(Rack::Files::Iterator) || value.is_a?(Stream) + headers.delete 'Content-Length' + end + response.body = value + else + response.body + end + end + + # Halt processing and redirect to the URI provided. + def redirect(uri, *args) + if (env['HTTP_VERSION'] == 'HTTP/1.1') && (env['REQUEST_METHOD'] != 'GET') + status 303 + else + status 302 + end + + # According to RFC 2616 section 14.30, "the field value consists of a + # single absolute URI" + response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?) + halt(*args) + end + + # Generates the absolute URI for a given path in the app. + # Takes Rack routers and reverse proxies into account. + def uri(addr = nil, absolute = true, add_script_name = true) + return addr if addr.to_s =~ /\A[a-z][a-z0-9+.\-]*:/i + + uri = [host = String.new] + if absolute + host << "http#{'s' if request.secure?}://" + host << if request.forwarded? || (request.port != (request.secure? ? 443 : 80)) + request.host_with_port + else + request.host + end + end + uri << request.script_name.to_s if add_script_name + uri << (addr || request.path_info).to_s + File.join uri + end + + alias url uri + alias to uri + + # Halt processing and return the error status provided. + def error(code, body = nil) + if code.respond_to? :to_str + body = code.to_str + code = 500 + end + response.body = body unless body.nil? + halt code + end + + # Halt processing and return a 404 Not Found. + def not_found(body = nil) + error 404, body + end + + # Set multiple response headers with Hash. + def headers(hash = nil) + response.headers.merge! hash if hash + response.headers + end + + # Access the underlying Rack session. + def session + request.session + end + + # Access shared logger object. + def logger + request.logger + end + + # Look up a media type by file extension in Rack's mime registry. + def mime_type(type) + Base.mime_type(type) + end + + # Set the Content-Type of the response body given a media type or file + # extension. + def content_type(type = nil, params = {}) + return response['Content-Type'] unless type + + default = params.delete :default + mime_type = mime_type(type) || default + raise format('Unknown media type: %p', type) if mime_type.nil? + + mime_type = mime_type.dup + unless params.include?(:charset) || settings.add_charset.all? { |p| !(p === mime_type) } + params[:charset] = params.delete('charset') || settings.default_encoding + end + params.delete :charset if mime_type.include? 'charset' + unless params.empty? + mime_type << (mime_type.include?(';') ? ', ' : ';') + mime_type << params.map do |key, val| + val = val.inspect if val =~ /[";,]/ + "#{key}=#{val}" + end.join(', ') + end + response['Content-Type'] = mime_type + end + + # https://html.spec.whatwg.org/#multipart-form-data + MULTIPART_FORM_DATA_REPLACEMENT_TABLE = { + '"' => '%22', + "\r" => '%0D', + "\n" => '%0A' + }.freeze + + # Set the Content-Disposition to "attachment" with the specified filename, + # instructing the user agents to prompt to save. + def attachment(filename = nil, disposition = :attachment) + response['Content-Disposition'] = disposition.to_s.dup + return unless filename + + params = format('; filename="%s"', File.basename(filename).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)) + response['Content-Disposition'] << params + ext = File.extname(filename) + content_type(ext) unless response['Content-Type'] || ext.empty? + end + + # Use the contents of the file at +path+ as the response body. + def send_file(path, opts = {}) + if opts[:type] || !response['Content-Type'] + content_type opts[:type] || File.extname(path), default: 'application/octet-stream' + end + + disposition = opts[:disposition] + filename = opts[:filename] + disposition = :attachment if disposition.nil? && filename + filename = path if filename.nil? + attachment(filename, disposition) if disposition + + last_modified opts[:last_modified] if opts[:last_modified] + + file = Rack::Files.new(File.dirname(settings.app_file)) + result = file.serving(request, path) + + result[1].each { |k, v| headers[k] ||= v } + headers['Content-Length'] = result[1]['Content-Length'] + opts[:status] &&= Integer(opts[:status]) + halt (opts[:status] || result[0]), result[2] + rescue Errno::ENOENT + not_found + end + + # Class of the response body in case you use #stream. + # + # Three things really matter: The front and back block (back being the + # block generating content, front the one sending it to the client) and + # the scheduler, integrating with whatever concurrency feature the Rack + # handler is using. + # + # Scheduler has to respond to defer and schedule. + class Stream + def self.schedule(*) yield end + def self.defer(*) yield end + + def initialize(scheduler = self.class, keep_open = false, &back) + @back = back.to_proc + @scheduler = scheduler + @keep_open = keep_open + @callbacks = [] + @closed = false + end + + def close + return if closed? + + @closed = true + @scheduler.schedule { @callbacks.each { |c| c.call } } + end + + def each(&front) + @front = front + @scheduler.defer do + begin + @back.call(self) + rescue Exception => e + @scheduler.schedule { raise e } + ensure + close unless @keep_open + end + end + end + + def <<(data) + @scheduler.schedule { @front.call(data.to_s) } + self + end + + def callback(&block) + return yield if closed? + + @callbacks << block + end + + alias errback callback + + def closed? + @closed + end + end + + # Allows to start sending data to the client even though later parts of + # the response body have not yet been generated. + # + # The close parameter specifies whether Stream#close should be called + # after the block has been executed. This is only relevant for evented + # servers like Rainbows. + def stream(keep_open = false) + scheduler = env['async.callback'] ? EventMachine : Stream + current = @params.dup + stream = if scheduler == Stream && keep_open + Stream.new(scheduler, false) do |out| + until out.closed? + with_params(current) { yield(out) } + end + end + else + Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } } + end + body stream + end + + # Specify response freshness policy for HTTP caches (Cache-Control header). + # Any number of non-value directives (:public, :private, :no_cache, + # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with + # a Hash of value directives (:max_age, :s_maxage). + # + # cache_control :public, :must_revalidate, :max_age => 60 + # => Cache-Control: public, must-revalidate, max-age=60 + # + # See RFC 2616 / 14.9 for more on standard cache control directives: + # http://tools.ietf.org/html/rfc2616#section-14.9.1 + def cache_control(*values) + if values.last.is_a?(Hash) + hash = values.pop + hash.reject! { |_k, v| v == false } + hash.reject! { |k, v| values << k if v == true } + else + hash = {} + end + + values.map! { |value| value.to_s.tr('_', '-') } + hash.each do |key, value| + key = key.to_s.tr('_', '-') + value = value.to_i if %w[max-age s-maxage].include? key + values << "#{key}=#{value}" + end + + response['Cache-Control'] = values.join(', ') if values.any? + end + + # Set the Expires header and Cache-Control/max-age directive. Amount + # can be an integer number of seconds in the future or a Time object + # indicating when the response should be considered "stale". The remaining + # "values" arguments are passed to the #cache_control helper: + # + # expires 500, :public, :must_revalidate + # => Cache-Control: public, must-revalidate, max-age=500 + # => Expires: Mon, 08 Jun 2009 08:50:17 GMT + # + def expires(amount, *values) + values << {} unless values.last.is_a?(Hash) + + if amount.is_a? Integer + time = Time.now + amount.to_i + max_age = amount + else + time = time_for amount + max_age = time - Time.now + end + + values.last.merge!(max_age: max_age) { |_key, v1, v2| v1 || v2 } + cache_control(*values) + + response['Expires'] = time.httpdate + end + + # Set the last modified time of the resource (HTTP 'Last-Modified' header) + # and halt if conditional GET matches. The +time+ argument is a Time, + # DateTime, or other object that responds to +to_time+. + # + # When the current request includes an 'If-Modified-Since' header that is + # equal or later than the time specified, execution is immediately halted + # with a '304 Not Modified' response. + def last_modified(time) + return unless time + + time = time_for time + response['Last-Modified'] = time.httpdate + return if env['HTTP_IF_NONE_MATCH'] + + if (status == 200) && env['HTTP_IF_MODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i + halt 304 if since >= time.to_i + end + + if (success? || (status == 412)) && env['HTTP_IF_UNMODIFIED_SINCE'] + # compare based on seconds since epoch + since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i + halt 412 if since < time.to_i + end + rescue ArgumentError + end + + ETAG_KINDS = %i[strong weak].freeze + # Set the response entity tag (HTTP 'ETag' header) and halt if conditional + # GET matches. The +value+ argument is an identifier that uniquely + # identifies the current version of the resource. The +kind+ argument + # indicates whether the etag should be used as a :strong (default) or :weak + # cache validator. + # + # When the current request includes an 'If-None-Match' header with a + # matching etag, execution is immediately halted. If the request method is + # GET or HEAD, a '304 Not Modified' response is sent. + def etag(value, options = {}) + # Before touching this code, please double check RFC 2616 14.24 and 14.26. + options = { kind: options } unless Hash === options + kind = options[:kind] || :strong + new_resource = options.fetch(:new_resource) { request.post? } + + unless ETAG_KINDS.include?(kind) + raise ArgumentError, ':strong or :weak expected' + end + + value = format('"%s"', value) + value = "W/#{value}" if kind == :weak + response['ETag'] = value + + return unless success? || status == 304 + + if etag_matches?(env['HTTP_IF_NONE_MATCH'], new_resource) + halt(request.safe? ? 304 : 412) + end + + if env['HTTP_IF_MATCH'] + return if etag_matches?(env['HTTP_IF_MATCH'], new_resource) + + halt 412 + end + + nil + end + + # Sugar for redirect (example: redirect back) + def back + request.referer + end + + # whether or not the status is set to 1xx + def informational? + status.between? 100, 199 + end + + # whether or not the status is set to 2xx + def success? + status.between? 200, 299 + end + + # whether or not the status is set to 3xx + def redirect? + status.between? 300, 399 + end + + # whether or not the status is set to 4xx + def client_error? + status.between? 400, 499 + end + + # whether or not the status is set to 5xx + def server_error? + status.between? 500, 599 + end + + # whether or not the status is set to 404 + def not_found? + status == 404 + end + + # whether or not the status is set to 400 + def bad_request? + status == 400 + end + + # Generates a Time object from the given value. + # Used by #expires and #last_modified. + def time_for(value) + if value.is_a? Numeric + Time.at value + elsif value.respond_to? :to_s + Time.parse value.to_s + else + value.to_time + end + rescue ArgumentError => e + raise e + rescue Exception + raise ArgumentError, "unable to convert #{value.inspect} to a Time object" + end + + private + + # Helper method checking if a ETag value list includes the current ETag. + def etag_matches?(list, new_resource = request.post?) + return !new_resource if list == '*' + + list.to_s.split(/\s*,\s*/).include? response['ETag'] + end + + def with_params(temp_params) + original = @params + @params = temp_params + yield + ensure + @params = original if original + end + end + + # Template rendering methods. Each method takes the name of a template + # to render as a Symbol and returns a String with the rendered output, + # as well as an optional hash with additional options. + # + # `template` is either the name or path of the template as symbol + # (Use `:'subdir/myview'` for views in subdirectories), or a string + # that will be rendered. + # + # Possible options are: + # :content_type The content type to use, same arguments as content_type. + # :layout If set to something falsy, no layout is rendered, otherwise + # the specified layout is used (Ignored for `sass`) + # :layout_engine Engine to use for rendering the layout. + # :locals A hash with local variables that should be available + # in the template + # :scope If set, template is evaluate with the binding of the given + # object rather than the application instance. + # :views Views directory to use. + module Templates + module ContentTyped + attr_accessor :content_type + end + + def initialize + super + @default_layout = :layout + @preferred_extension = nil + end + + def erb(template, options = {}, locals = {}, &block) + render(:erb, template, options, locals, &block) + end + + def haml(template, options = {}, locals = {}, &block) + render(:haml, template, options, locals, &block) + end + + def sass(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :sass, template, options, locals + end + + def scss(template, options = {}, locals = {}) + options[:default_content_type] = :css + options[:exclude_outvar] = true + options[:layout] = nil + render :scss, template, options, locals + end + + def builder(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:builder, template, options, locals, &block) + end + + def liquid(template, options = {}, locals = {}, &block) + render(:liquid, template, options, locals, &block) + end + + def markdown(template, options = {}, locals = {}) + options[:exclude_outvar] = true + render :markdown, template, options, locals + end + + def rdoc(template, options = {}, locals = {}) + render :rdoc, template, options, locals + end + + def asciidoc(template, options = {}, locals = {}) + render :asciidoc, template, options, locals + end + + def markaby(template = nil, options = {}, locals = {}, &block) + render_ruby(:mab, template, options, locals, &block) + end + + def nokogiri(template = nil, options = {}, locals = {}, &block) + options[:default_content_type] = :xml + render_ruby(:nokogiri, template, options, locals, &block) + end + + def slim(template, options = {}, locals = {}, &block) + render(:slim, template, options, locals, &block) + end + + def yajl(template, options = {}, locals = {}) + options[:default_content_type] = :json + render :yajl, template, options, locals + end + + def rabl(template, options = {}, locals = {}) + Rabl.register! + render :rabl, template, options, locals + end + + # Calls the given block for every possible template file in views, + # named name.ext, where ext is registered on engine. + def find_template(views, name, engine) + yield ::File.join(views, "#{name}.#{@preferred_extension}") + + Tilt.default_mapping.extensions_for(engine).each do |ext| + yield ::File.join(views, "#{name}.#{ext}") unless ext == @preferred_extension + end + end + + private + + # logic shared between builder and nokogiri + def render_ruby(engine, template, options = {}, locals = {}, &block) + if template.is_a?(Hash) + options = template + template = nil + end + template = proc { block } if template.nil? + render engine, template, options, locals + end + + def render(engine, data, options = {}, locals = {}, &block) + # merge app-level options + engine_options = settings.respond_to?(engine) ? settings.send(engine) : {} + options.merge!(engine_options) { |_key, v1, _v2| v1 } + + # extract generic options + locals = options.delete(:locals) || locals || {} + views = options.delete(:views) || settings.views || './views' + layout = options[:layout] + layout = false if layout.nil? && options.include?(:layout) + eat_errors = layout.nil? + layout = engine_options[:layout] if layout.nil? || (layout == true && engine_options[:layout] != false) + layout = @default_layout if layout.nil? || (layout == true) + layout_options = options.delete(:layout_options) || {} + content_type = options.delete(:default_content_type) + content_type = options.delete(:content_type) || content_type + layout_engine = options.delete(:layout_engine) || engine + scope = options.delete(:scope) || self + exclude_outvar = options.delete(:exclude_outvar) + options.delete(:layout) + + # set some defaults + options[:outvar] ||= '@_out_buf' unless exclude_outvar + options[:default_encoding] ||= settings.default_encoding + + # compile and render template + begin + layout_was = @default_layout + @default_layout = false + template = compile_template(engine, data, options, views) + output = template.render(scope, locals, &block) + ensure + @default_layout = layout_was + end + + # render layout + if layout + extra_options = { views: views, layout: false, eat_errors: eat_errors, scope: scope } + options = options.merge(extra_options).merge!(layout_options) + + catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } } + end + + if content_type + # sass-embedded returns a frozen string + output = +output + output.extend(ContentTyped).content_type = content_type + end + output + end + + def compile_template(engine, data, options, views) + eat_errors = options.delete :eat_errors + template = Tilt[engine] + raise "Template engine not found: #{engine}" if template.nil? + + case data + when Symbol + template_cache.fetch engine, data, options, views do + body, path, line = settings.templates[data] + if body + body = body.call if body.respond_to?(:call) + template.new(path, line.to_i, options) { body } + else + found = false + @preferred_extension = engine.to_s + find_template(views, data, template) do |file| + path ||= file # keep the initial path rather than the last one + found = File.exist?(file) + if found + path = file + break + end + end + throw :layout_missing if eat_errors && !found + template.new(path, 1, options) + end + end + when Proc + compile_block_template(template, options, &data) + when String + template_cache.fetch engine, data, options, views do + compile_block_template(template, options) { data } + end + else + raise ArgumentError, "Sorry, don't know how to render #{data.inspect}." + end + end + + def compile_block_template(template, options, &body) + first_location = caller_locations.first + path = first_location.path + line = first_location.lineno + path = options[:path] || path + line = options[:line] || line + template.new(path, line.to_i, options, &body) + end + end + + # Extremely simple template cache implementation. + # * Not thread-safe. + # * Size is unbounded. + # * Keys are not copied defensively, and should not be modified after + # being passed to #fetch. More specifically, the values returned by + # key#hash and key#eql? should not change. + # + # Implementation copied from Tilt::Cache. + class TemplateCache + def initialize + @cache = {} + end + + # Caches a value for key, or returns the previously cached value. + # If a value has been previously cached for key then it is + # returned. Otherwise, block is yielded to and its return value + # which may be nil, is cached under key and returned. + def fetch(*key) + @cache.fetch(key) do + @cache[key] = yield + end + end + + # Clears the cache. + def clear + @cache = {} + end + end + + # Base class for all Sinatra applications and middleware. + class Base + include Rack::Utils + include Helpers + include Templates + + URI_INSTANCE = URI::Parser.new + + attr_accessor :app, :env, :request, :response, :params + attr_reader :template_cache + + def initialize(app = nil, **_kwargs) + super() + @app = app + @template_cache = TemplateCache.new + @pinned_response = nil # whether a before! filter pinned the content-type + yield self if block_given? + end + + # Rack call interface. + def call(env) + dup.call!(env) + end + + def call!(env) # :nodoc: + @env = env + @params = IndifferentHash.new + @request = Request.new(env) + @response = Response.new + @pinned_response = nil + template_cache.clear if settings.reload_templates + + invoke { dispatch! } + invoke { error_block!(response.status) } unless @env['sinatra.error'] + + unless @response['Content-Type'] + if Array === body && body[0].respond_to?(:content_type) + content_type body[0].content_type + elsif (default = settings.default_content_type) + content_type default + end + end + + @response.finish + end + + # Access settings defined with Base.set. + def self.settings + self + end + + # Access settings defined with Base.set. + def settings + self.class.settings + end + + # Exit the current block, halts any further processing + # of the request, and returns the specified response. + def halt(*response) + response = response.first if response.length == 1 + throw :halt, response + end + + # Pass control to the next matching route. + # If there are no more matching routes, Sinatra will + # return a 404 response. + def pass(&block) + throw :pass, block + end + + # Forward the request to the downstream app -- middleware only. + def forward + raise 'downstream app not set' unless @app.respond_to? :call + + status, headers, body = @app.call env + @response.status = status + @response.body = body + @response.headers.merge! headers + nil + end + + private + + # Run filters defined on the class and all superclasses. + # Accepts an optional block to call after each filter is applied. + def filter!(type, base = settings, &block) + filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters) + base.filters[type].each do |args| + result = process_route(*args) + block.call(result) if block_given? + end + end + + # Run routes defined on the class and all superclasses. + def route!(base = settings, pass_block = nil) + routes = base.routes[@request.request_method] + + routes&.each do |pattern, conditions, block| + response.delete_header('Content-Type') unless @pinned_response + + returned_pass_block = process_route(pattern, conditions) do |*args| + env['sinatra.route'] = "#{@request.request_method} #{pattern}" + route_eval { block[*args] } + end + + # don't wipe out pass_block in superclass + pass_block = returned_pass_block if returned_pass_block + end + + # Run routes defined in superclass. + if base.superclass.respond_to?(:routes) + return route!(base.superclass, pass_block) + end + + route_eval(&pass_block) if pass_block + route_missing + end + + # Run a route block and throw :halt with the result. + def route_eval + throw :halt, yield + end + + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, conditions, block = nil, values = []) + route = @request.path_info + route = '/' if route.empty? && !settings.empty_path_info? + route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/') + + params = pattern.params(route) + return unless params + + params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes + force_encoding(params) + @params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any? + + regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) }) + if regexp_exists + captures = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c } + values += captures + @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty? + else + values += params.values.flatten + end + + catch(:pass) do + conditions.each { |c| throw :pass if c.bind(self).call == false } + block ? block[self, values] : yield(self, values) + end + rescue StandardError + @env['sinatra.error.params'] = @params + raise + ensure + params ||= {} + params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params'] + end + + # No matching route was found or all routes passed. The default + # implementation is to forward the request downstream when running + # as middleware (@app is non-nil); when no downstream app is set, raise + # a NotFound exception. Subclasses can override this method to perform + # custom route miss logic. + def route_missing + raise NotFound unless @app + + forward + end + + # Attempt to serve static files from public directory. Throws :halt when + # a matching file is found, returns nil otherwise. + def static!(options = {}) + return if (public_dir = settings.public_folder).nil? + + path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" + return unless valid_path?(path) + + path = File.expand_path(path) + return unless path.start_with?("#{File.expand_path(public_dir)}/") + + return unless File.file?(path) + + env['sinatra.static_file'] = path + cache_control(*settings.static_cache_control) if settings.static_cache_control? + send_file path, options.merge(disposition: nil) + end + + # Run the block with 'throw :halt' support and apply result to the response. + def invoke(&block) + res = catch(:halt, &block) + + res = [res] if (Integer === res) || (String === res) + if (Array === res) && (Integer === res.first) + res = res.dup + status(res.shift) + body(res.pop) + headers(*res) + elsif res.respond_to? :each + body res + end + nil # avoid double setting the same response tuple twice + end + + # Dispatch a request with error handling. + def dispatch! + # Avoid passing frozen string in force_encoding + @params.merge!(@request.params).each do |key, val| + next unless val.respond_to?(:force_encoding) + + val = val.dup if val.frozen? + @params[key] = force_encoding(val) + end + + invoke do + static! if settings.static? && (request.get? || request.head?) + filter! :before do + @pinned_response = !response['Content-Type'].nil? + end + route! + end + rescue ::Exception => e + invoke { handle_exception!(e) } + ensure + begin + filter! :after unless env['sinatra.static_file'] + rescue ::Exception => e + invoke { handle_exception!(e) } unless @env['sinatra.error'] + end + end + + # Error handling during requests. + def handle_exception!(boom) + error_params = @env['sinatra.error.params'] + + @params = @params.merge(error_params) if error_params + + @env['sinatra.error'] = boom + + http_status = if boom.is_a? Sinatra::Error + if boom.respond_to? :http_status + boom.http_status + elsif settings.use_code? && boom.respond_to?(:code) + boom.code + end + end + + http_status = 500 unless http_status&.between?(400, 599) + status(http_status) + + if server_error? + dump_errors! boom if settings.dump_errors? + raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler) + elsif not_found? + headers['X-Cascade'] = 'pass' if settings.x_cascade? + end + + if (res = error_block!(boom.class, boom) || error_block!(status, boom)) + return res + end + + if not_found? || bad_request? + if boom.message && boom.message != boom.class.name + body Rack::Utils.escape_html(boom.message) + else + content_type 'text/html' + body "

#{not_found? ? 'Not Found' : 'Bad Request'}

" + end + end + + return unless server_error? + + raise boom if settings.raise_errors? || settings.show_exceptions? + + error_block! Exception, boom + end + + # Find an custom error block for the key(s) specified. + def error_block!(key, *block_params) + base = settings + while base.respond_to?(:errors) + args_array = base.errors[key] + + next base = base.superclass unless args_array + + args_array.reverse_each do |args| + first = args == args_array.first + args += [block_params] + resp = process_route(*args) + return resp unless resp.nil? && !first + end + end + return false unless key.respond_to?(:superclass) && (key.superclass < Exception) + + error_block!(key.superclass, *block_params) + end + + def dump_errors!(boom) + if boom.respond_to?(:detailed_message) + msg = boom.detailed_message(highlight: false) + if msg =~ /\A(.*?)(?: \(#{ Regexp.quote(boom.class.to_s) }\))?\n/ + msg = $1 + additional_msg = $'.lines(chomp: true) + else + additional_msg = [] + end + else + msg = boom.message + additional_msg = [] + end + msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{msg}:", *additional_msg, *boom.backtrace].join("\n\t") + @env['rack.errors'].puts(msg) + end + + class << self + CALLERS_TO_IGNORE = [ # :nodoc: + %r{/sinatra(/(base|main|show_exceptions))?\.rb$}, # all sinatra code + %r{lib/tilt.*\.rb$}, # all tilt code + /^\(.*\)$/, # generated code + %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks + /active_support/, # active_support require hacks + %r{bundler(/(?:runtime|inline))?\.rb}, # bundler require hacks + /= 1.9.2 + %r{zeitwerk/kernel\.rb} # Zeitwerk kernel#require decorator + ].freeze + + attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback + + def callers_to_ignore + CALLERS_TO_IGNORE + end + + # Removes all routes, filters, middleware and extension hooks from the + # current class (not routes/filters/... defined by its superclass). + def reset! + @conditions = [] + @routes = {} + @filters = { before: [], after: [] } + @errors = {} + @middleware = [] + @prototype = nil + @extensions = [] + + @templates = if superclass.respond_to?(:templates) + Hash.new { |_hash, key| superclass.templates[key] } + else + {} + end + end + + # Extension modules registered on this class and all superclasses. + def extensions + if superclass.respond_to?(:extensions) + (@extensions + superclass.extensions).uniq + else + @extensions + end + end + + # Middleware used in this class and all superclasses. + def middleware + if superclass.respond_to?(:middleware) + superclass.middleware + @middleware + else + @middleware + end + end + + # Sets an option to the given value. If the value is a proc, + # the proc will be called every time the option is accessed. + def set(option, value = (not_set = true), ignore_setter = false, &block) + raise ArgumentError if block && !not_set + + if block + value = block + not_set = false + end + + if not_set + raise ArgumentError unless option.respond_to?(:each) + + option.each { |k, v| set(k, v) } + return self + end + + if respond_to?("#{option}=") && !ignore_setter + return __send__("#{option}=", value) + end + + setter = proc { |val| set option, val, true } + getter = proc { value } + + case value + when Proc + getter = value + when Symbol, Integer, FalseClass, TrueClass, NilClass + getter = value.inspect + when Hash + setter = proc do |val| + val = value.merge val if Hash === val + set option, val, true + end + end + + define_singleton("#{option}=", setter) + define_singleton(option, getter) + define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?" + self + end + + # Same as calling `set :option, true` for each of the given options. + def enable(*opts) + opts.each { |key| set(key, true) } + end + + # Same as calling `set :option, false` for each of the given options. + def disable(*opts) + opts.each { |key| set(key, false) } + end + + # Define a custom error handler. Optionally takes either an Exception + # class, or an HTTP status code to specify which errors should be + # handled. + def error(*codes, &block) + args = compile! 'ERROR', /.*/, block + codes = codes.flat_map(&method(:Array)) + codes << Exception if codes.empty? + codes << Sinatra::NotFound if codes.include?(404) + codes.each { |c| (@errors[c] ||= []) << args } + end + + # Sugar for `error(404) { ... }` + def not_found(&block) + error(404, &block) + end + + # Define a named template. The block must return the template source. + def template(name, &block) + filename, line = caller_locations.first + templates[name] = [block, filename, line.to_i] + end + + # Define the layout template. The block must return the template source. + def layout(name = :layout, &block) + template name, &block + end + + # Load embedded templates from the file; uses the caller's __FILE__ + # when no file is specified. + def inline_templates=(file = nil) + file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true + + begin + io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file) + app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2) + rescue Errno::ENOENT + app, data = nil + end + + return unless data + + encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m + $2 + else + settings.default_encoding + end + + lines = app.count("\n") + 1 + template = nil + force_encoding data, encoding + data.each_line do |line| + lines += 1 + if line =~ /^@@\s*(.*\S)\s*$/ + template = force_encoding(String.new, encoding) + templates[$1.to_sym] = [template, file, lines] + elsif template + template << line + end + end + end + + # Lookup or register a mime type in Rack's mime registry. + def mime_type(type, value = nil) + return type if type.nil? + return type.to_s if type.to_s.include?('/') + + type = ".#{type}" unless type.to_s[0] == '.' + return Rack::Mime.mime_type(type, nil) unless value + + Rack::Mime::MIME_TYPES[type] = value + end + + # provides all mime types matching type, including deprecated types: + # mime_types :html # => ['text/html'] + # mime_types :js # => ['application/javascript', 'text/javascript'] + def mime_types(type) + type = mime_type type + type =~ %r{^application/(xml|javascript)$} ? [type, "text/#{$1}"] : [type] + end + + # Define a before filter; runs before all requests within the same + # context as route handlers and may access/modify the request and + # response. + def before(path = /.*/, **options, &block) + add_filter(:before, path, **options, &block) + end + + # Define an after filter; runs after all requests within the same + # context as route handlers and may access/modify the request and + # response. + def after(path = /.*/, **options, &block) + add_filter(:after, path, **options, &block) + end + + # add a filter + def add_filter(type, path = /.*/, **options, &block) + filters[type] << compile!(type, path, block, **options) + end + + def on_start(&on_start_callback) + @on_start_callback = on_start_callback + end + + def on_stop(&on_stop_callback) + @on_stop_callback = on_stop_callback + end + + # Add a route condition. The route is considered non-matching when the + # block returns false. + def condition(name = "#{caller.first[/`.*'/]} condition", &block) + @conditions << generate_method(name, &block) + end + + def public=(value) + warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead' + set(:public_folder, value) + end + + def public_dir=(value) + self.public_folder = value + end + + def public_dir + public_folder + end + + # Defining a `GET` handler also automatically defines + # a `HEAD` handler. + def get(path, opts = {}, &block) + conditions = @conditions.dup + route('GET', path, opts, &block) + + @conditions = conditions + route('HEAD', path, opts, &block) + end + + def put(path, opts = {}, &block) route 'PUT', path, opts, &block end + + def post(path, opts = {}, &block) route 'POST', path, opts, &block end + + def delete(path, opts = {}, &block) route 'DELETE', path, opts, &block end + + def head(path, opts = {}, &block) route 'HEAD', path, opts, &block end + + def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end + + def patch(path, opts = {}, &block) route 'PATCH', path, opts, &block end + + def link(path, opts = {}, &block) route 'LINK', path, opts, &block end + + def unlink(path, opts = {}, &block) route 'UNLINK', path, opts, &block end + + # Makes the methods defined in the block and in the Modules given + # in `extensions` available to the handlers and templates + def helpers(*extensions, &block) + class_eval(&block) if block_given? + include(*extensions) if extensions.any? + end + + # Register an extension. Alternatively take a block from which an + # extension will be created and registered on the fly. + def register(*extensions, &block) + extensions << Module.new(&block) if block_given? + @extensions += extensions + extensions.each do |extension| + extend extension + extension.registered(self) if extension.respond_to?(:registered) + end + end + + def development?; environment == :development end + def production?; environment == :production end + def test?; environment == :test end + + # Set configuration options for Sinatra and/or the app. + # Allows scoping of settings for certain environments. + def configure(*envs) + yield self if envs.empty? || envs.include?(environment.to_sym) + end + + # Use the specified Rack middleware + def use(middleware, *args, &block) + @prototype = nil + @middleware << [middleware, args, block] + end + ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true) + + # Stop the self-hosted server if running. + def quit! + return unless running? + + # Use Thin's hard #stop! if available, otherwise just #stop. + running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop + warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages? + set :running_server, nil + set :handler_name, nil + + on_stop_callback.call unless on_stop_callback.nil? + end + + alias stop! quit! + + # Run the Sinatra app as a self-hosted server using + # Puma, Falcon, or WEBrick (in that order). If given a block, will call + # with the constructed handler once we have taken the stage. + def run!(options = {}, &block) + return if running? + + set options + handler = Rack::Handler.pick(server) + handler_name = handler.name.gsub(/.*::/, '') + server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {} + server_settings.merge!(Port: port, Host: bind) + + begin + start_server(handler, server_settings, handler_name, &block) + rescue Errno::EADDRINUSE + warn "== Someone is already performing on port #{port}!" + raise + ensure + quit! + end + end + + alias start! run! + + # Check whether the self-hosted server is running or not. + def running? + running_server? + end + + # The prototype instance used to process requests. + def prototype + @prototype ||= new + end + + # Create a new instance without middleware in front of it. + alias new! new unless method_defined? :new! + + # Create a new instance of the class fronted by its middleware + # pipeline. The object is guaranteed to respond to #call but may not be + # an instance of the class new was called on. + def new(*args, &block) + instance = new!(*args, &block) + Wrapper.new(build(instance).to_app, instance) + end + ruby2_keywords :new if respond_to?(:ruby2_keywords, true) + + # Creates a Rack::Builder instance with all the middleware set up and + # the given +app+ as end point. + def build(app) + builder = Rack::Builder.new + setup_default_middleware builder + setup_middleware builder + builder.run app + builder + end + + def call(env) + synchronize { prototype.call(env) } + end + + # Like Kernel#caller but excluding certain magic entries and without + # line / method information; the resulting array contains filenames only. + def caller_files + cleaned_caller(1).flatten + end + + private + + # Starts the server by running the Rack Handler. + def start_server(handler, server_settings, handler_name) + # Ensure we initialize middleware before startup, to match standard Rack + # behavior, by ensuring an instance exists: + prototype + # Run the instance we created: + handler.run(self, **server_settings) do |server| + unless suppress_messages? + warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}" + end + + setup_traps + set :running_server, server + set :handler_name, handler_name + server.threaded = settings.threaded if server.respond_to? :threaded= + on_start_callback.call unless on_start_callback.nil? + yield server if block_given? + end + end + + def suppress_messages? + handler_name =~ /cgi/i || quiet + end + + def setup_traps + return unless traps? + + at_exit { quit! } + + %i[INT TERM].each do |signal| + old_handler = trap(signal) do + quit! + old_handler.call if old_handler.respond_to?(:call) + end + end + + set :traps, false + end + + # Dynamically defines a method on settings. + def define_singleton(name, content = Proc.new) + singleton_class.class_eval do + undef_method(name) if method_defined? name + String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content) + end + end + + # Condition for matching host name. Parameter might be String or Regexp. + def host_name(pattern) + condition { pattern === request.host } + end + + # Condition for matching user agent. Parameter should be Regexp. + # Will set params[:agent]. + def user_agent(pattern) + condition do + if request.user_agent.to_s =~ pattern + @params[:agent] = $~[1..-1] + true + else + false + end + end + end + alias agent user_agent + + # Condition for matching mimetypes. Accepts file extensions. + def provides(*types) + types.map! { |t| mime_types(t) } + types.flatten! + condition do + response_content_type = response['Content-Type'] + preferred_type = request.preferred_type(types) + + if response_content_type + types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/]) + elsif preferred_type + params = (preferred_type.respond_to?(:params) ? preferred_type.params : {}) + content_type(preferred_type, params) + true + else + false + end + end + end + + def route(verb, path, options = {}, &block) + enable :empty_path_info if path == '' && empty_path_info.nil? + signature = compile!(verb, path, block, **options) + (@routes[verb] ||= []) << signature + invoke_hook(:route_added, verb, path, block) + signature + end + + def invoke_hook(name, *args) + extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } + end + + def generate_method(method_name, &block) + define_method(method_name, &block) + method = instance_method method_name + remove_method method_name + method + end + + def compile!(verb, path, block, **options) + # Because of self.options.host + host_name(options.delete(:host)) if options.key?(:host) + # Pass Mustermann opts to compile() + route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze + + options.each_pair { |option, args| send(option, *args) } + + pattern = compile(path, route_mustermann_opts) + method_name = "#{verb} #{path}" + unbound_method = generate_method(method_name, &block) + conditions = @conditions + @conditions = [] + wrapper = block.arity.zero? ? + proc { |a, _p| unbound_method.bind(a).call } : + proc { |a, p| unbound_method.bind(a).call(*p) } + + [pattern, conditions, wrapper] + end + + def compile(path, route_mustermann_opts = {}) + Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts)) + end + + def setup_default_middleware(builder) + builder.use ExtendedRack + builder.use ShowExceptions if show_exceptions? + builder.use Rack::MethodOverride if method_override? + builder.use Rack::Head + setup_logging builder + setup_sessions builder + setup_protection builder + end + + def setup_middleware(builder) + middleware.each { |c, a, b| builder.use(c, *a, &b) } + end + + def setup_logging(builder) + if logging? + setup_common_logger(builder) + setup_custom_logger(builder) + elsif logging == false + setup_null_logger(builder) + end + end + + def setup_null_logger(builder) + builder.use Rack::NullLogger + end + + def setup_common_logger(builder) + builder.use Sinatra::CommonLogger + end + + def setup_custom_logger(builder) + if logging.respond_to? :to_int + builder.use Rack::Logger, logging + else + builder.use Rack::Logger + end + end + + def setup_protection(builder) + return unless protection? + + options = Hash === protection ? protection.dup : {} + options = { + img_src: "'self' data:", + font_src: "'self'" + }.merge options + + protect_session = options.fetch(:session) { sessions? } + options[:without_session] = !protect_session + + options[:reaction] ||= :drop_session + + builder.use Rack::Protection, options + end + + def setup_sessions(builder) + return unless sessions? + + options = {} + options[:secret] = session_secret if session_secret? + options.merge! sessions.to_hash if sessions.respond_to? :to_hash + builder.use session_store, options + end + + def inherited(subclass) + subclass.reset! + subclass.set :app_file, caller_files.first unless subclass.app_file? + super + end + + @@mutex = Mutex.new + def synchronize(&block) + if lock? + @@mutex.synchronize(&block) + else + yield + end + end + + # used for deprecation warnings + def warn_for_deprecation(message) + warn message + "\n\tfrom #{cleaned_caller.first.join(':')}" + end + + # Like Kernel#caller but excluding certain magic entries + def cleaned_caller(keep = 3) + caller(1) + .map! { |line| line.split(/:(?=\d|in )/, 3)[0, keep] } + .reject { |file, *_| callers_to_ignore.any? { |pattern| file =~ pattern } } + end + end + + # Force data to specified encoding. It defaults to settings.default_encoding + # which is UTF-8 by default + def self.force_encoding(data, encoding = default_encoding) + return if data == settings || data.is_a?(Tempfile) + + if data.respond_to? :force_encoding + data.force_encoding(encoding).encode! + elsif data.respond_to? :each_value + data.each_value { |v| force_encoding(v, encoding) } + elsif data.respond_to? :each + data.each { |v| force_encoding(v, encoding) } + end + data + end + + def force_encoding(*args) + settings.force_encoding(*args) + end + + reset! + + set :environment, (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym + set :raise_errors, proc { test? } + set :dump_errors, proc { !test? } + set :show_exceptions, proc { development? } + set :sessions, false + set :session_store, Rack::Protection::EncryptedCookie + set :logging, false + set :protection, true + set :method_override, false + set :use_code, false + set :default_encoding, 'utf-8' + set :x_cascade, true + set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" } + settings.add_charset << %r{^text/} + set :mustermann_opts, {} + set :default_content_type, 'text/html' + + # explicitly generating a session secret eagerly to play nice with preforking + begin + require 'securerandom' + set :session_secret, SecureRandom.hex(64) + rescue LoadError, NotImplementedError, RuntimeError + # SecureRandom raises a NotImplementedError if no random device is available + # RuntimeError raised due to broken openssl backend: https://bugs.ruby-lang.org/issues/19230 + set :session_secret, format('%064x', Kernel.rand((2**256) - 1)) + end + + class << self + alias methodoverride? method_override? + alias methodoverride= method_override= + end + + set :run, false # start server via at-exit hook? + set :running_server, nil + set :handler_name, nil + set :traps, true + set :server, %w[HTTP webrick] + set :bind, proc { development? ? 'localhost' : '0.0.0.0' } + set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567) + set :quiet, false + + ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE + + server.unshift 'thin' if ruby_engine != 'jruby' + server.unshift 'falcon' if ruby_engine != 'jruby' + server.unshift 'trinidad' if ruby_engine == 'jruby' + server.unshift 'puma' + + set :absolute_redirects, true + set :prefixed_redirects, false + set :empty_path_info, nil + set :strict_paths, true + + set :app_file, nil + set :root, proc { app_file && File.expand_path(File.dirname(app_file)) } + set :views, proc { root && File.join(root, 'views') } + set :reload_templates, proc { development? } + set :lock, false + set :threaded, true + + set :public_folder, proc { root && File.join(root, 'public') } + set :static, proc { public_folder && File.exist?(public_folder) } + set :static_cache_control, false + + error ::Exception do + response.status = 500 + content_type 'text/html' + '

Internal Server Error

' + end + + configure :development do + get '/__sinatra__/:image.png' do + filename = __dir__ + "/images/#{params[:image].to_i}.png" + content_type :png + send_file filename + end + + error NotFound do + content_type 'text/html' + + if instance_of?(Sinatra::Application) + code = <<-RUBY.gsub(/^ {12}/, '') + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + RUBY + else + code = <<-RUBY.gsub(/^ {12}/, '') + class #{self.class} + #{request.request_method.downcase} '#{request.path_info}' do + "Hello World" + end + end + RUBY + + file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(%r{^/}, '') + code = "# in #{file}\n#{code}" unless file.empty? + end + + <<-HTML.gsub(/^ {10}/, '') + + + + + + +

Sinatra doesn’t know this ditty.

+ +
+ Try this: +
#{Rack::Utils.escape_html(code)}
+
+ + + HTML + end + end + end + + # Execution context for classic style (top-level) applications. All + # DSL methods executed on main are delegated to this class. + # + # The Application class should not be subclassed, unless you want to + # inherit all settings, routes, handlers, and error pages from the + # top-level. Subclassing Sinatra::Base is highly recommended for + # modular applications. + class Application < Base + set :logging, proc { !test? } + set :method_override, true + set :run, proc { !test? } + set :app_file, nil + + def self.register(*extensions, &block) # :nodoc: + added_methods = extensions.flat_map(&:public_instance_methods) + Delegator.delegate(*added_methods) + super(*extensions, &block) + end + end + + # Sinatra delegation mixin. Mixing this module into an object causes all + # methods to be delegated to the Sinatra::Application class. Used primarily + # at the top-level. + module Delegator # :nodoc: + def self.delegate(*methods) + methods.each do |method_name| + define_method(method_name) do |*args, &block| + return super(*args, &block) if respond_to? method_name + + Delegator.target.send(method_name, *args, &block) + end + # ensure keyword argument passing is compatible with ruby >= 2.7 + ruby2_keywords(method_name) if respond_to?(:ruby2_keywords, true) + private method_name + end + end + + delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink, + :template, :layout, :before, :after, :error, :not_found, :configure, + :set, :mime_type, :enable, :disable, :use, :development?, :test?, + :production?, :helpers, :settings, :register, :on_start, :on_stop + + class << self + attr_accessor :target + end + + self.target = Application + end + + class Wrapper + def initialize(stack, instance) + @stack = stack + @instance = instance + end + + def settings + @instance.settings + end + + def helpers + @instance + end + + def call(env) + @stack.call(env) + end + + def inspect + "#<#{@instance.class} app_file=#{settings.app_file.inspect}>" + end + end + + # Create a new Sinatra application; the block is evaluated in the class scope. + def self.new(base = Base, &block) + base = Class.new(base) + base.class_eval(&block) if block_given? + base + end + + # Extend the top-level DSL with the modules provided. + def self.register(*extensions, &block) + Delegator.target.register(*extensions, &block) + end + + # Include the helper modules provided in Sinatra's request context. + def self.helpers(*extensions, &block) + Delegator.target.helpers(*extensions, &block) + end + + # Use the middleware for classic applications. + def self.use(*args, &block) + Delegator.target.use(*args, &block) + end +end diff --git a/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/old.rb b/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/old.rb new file mode 100644 index 0000000..1becba2 --- /dev/null +++ b/tests/testdata/ruby_sinatra_use_exception_detailed_message_10/old.rb @@ -0,0 +1 @@ +404: Not Found \ No newline at end of file