mustermann 1.0.1 → 1.0.2.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +18 -0
  3. data/.rspec +5 -0
  4. data/.travis.yml +25 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +7 -0
  7. data/README.md +230 -799
  8. data/Rakefile +27 -0
  9. data/mustermann-contrib/LICENSE +23 -0
  10. data/mustermann-contrib/README.md +1155 -0
  11. data/mustermann-contrib/examples/highlighting.rb +35 -0
  12. data/mustermann-contrib/highlighting.png +0 -0
  13. data/mustermann-contrib/irb.png +0 -0
  14. data/mustermann-contrib/lib/mustermann/cake.rb +19 -0
  15. data/mustermann-contrib/lib/mustermann/express.rb +38 -0
  16. data/mustermann-contrib/lib/mustermann/file_utils.rb +218 -0
  17. data/mustermann-contrib/lib/mustermann/file_utils/glob_pattern.rb +40 -0
  18. data/mustermann-contrib/lib/mustermann/fileutils.rb +1 -0
  19. data/mustermann-contrib/lib/mustermann/flask.rb +199 -0
  20. data/mustermann-contrib/lib/mustermann/pyramid.rb +29 -0
  21. data/mustermann-contrib/lib/mustermann/rails.rb +47 -0
  22. data/mustermann-contrib/lib/mustermann/shell.rb +57 -0
  23. data/mustermann-contrib/lib/mustermann/simple.rb +51 -0
  24. data/mustermann-contrib/lib/mustermann/string_scanner.rb +314 -0
  25. data/mustermann-contrib/lib/mustermann/strscan.rb +1 -0
  26. data/mustermann-contrib/lib/mustermann/template.rb +63 -0
  27. data/mustermann-contrib/lib/mustermann/uri_template.rb +1 -0
  28. data/mustermann-contrib/lib/mustermann/versions.rb +47 -0
  29. data/mustermann-contrib/lib/mustermann/visualizer.rb +39 -0
  30. data/mustermann-contrib/lib/mustermann/visualizer/highlight.rb +138 -0
  31. data/mustermann-contrib/lib/mustermann/visualizer/highlighter.rb +38 -0
  32. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/ad_hoc.rb +95 -0
  33. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/ast.rb +103 -0
  34. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/composite.rb +46 -0
  35. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/dummy.rb +19 -0
  36. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/regular.rb +105 -0
  37. data/mustermann-contrib/lib/mustermann/visualizer/pattern_extension.rb +69 -0
  38. data/mustermann-contrib/lib/mustermann/visualizer/renderer/ansi.rb +24 -0
  39. data/mustermann-contrib/lib/mustermann/visualizer/renderer/generic.rb +47 -0
  40. data/mustermann-contrib/lib/mustermann/visualizer/renderer/hansi_template.rb +35 -0
  41. data/mustermann-contrib/lib/mustermann/visualizer/renderer/html.rb +51 -0
  42. data/mustermann-contrib/lib/mustermann/visualizer/renderer/sexp.rb +38 -0
  43. data/mustermann-contrib/lib/mustermann/visualizer/tree.rb +64 -0
  44. data/mustermann-contrib/lib/mustermann/visualizer/tree_renderer.rb +79 -0
  45. data/mustermann-contrib/mustermann-contrib.gemspec +19 -0
  46. data/mustermann-contrib/spec/cake_spec.rb +91 -0
  47. data/mustermann-contrib/spec/express_spec.rb +210 -0
  48. data/mustermann-contrib/spec/file_utils_spec.rb +120 -0
  49. data/mustermann-contrib/spec/flask_spec.rb +362 -0
  50. data/mustermann-contrib/spec/flask_subclass_spec.rb +369 -0
  51. data/mustermann-contrib/spec/pattern_extension_spec.rb +50 -0
  52. data/mustermann-contrib/spec/pyramid_spec.rb +102 -0
  53. data/mustermann-contrib/spec/rails_spec.rb +648 -0
  54. data/mustermann-contrib/spec/shell_spec.rb +148 -0
  55. data/mustermann-contrib/spec/simple_spec.rb +269 -0
  56. data/mustermann-contrib/spec/string_scanner_spec.rb +272 -0
  57. data/mustermann-contrib/spec/template_spec.rb +842 -0
  58. data/mustermann-contrib/spec/visualizer_spec.rb +199 -0
  59. data/mustermann-contrib/theme.png +0 -0
  60. data/mustermann-contrib/tree.png +0 -0
  61. data/mustermann/LICENSE +23 -0
  62. data/mustermann/README.md +853 -0
  63. data/{bench → mustermann/bench}/capturing.rb +0 -0
  64. data/{bench → mustermann/bench}/regexp.rb +0 -0
  65. data/{bench → mustermann/bench}/simple_vs_sinatra.rb +0 -0
  66. data/{bench → mustermann/bench}/template_vs_addressable.rb +0 -0
  67. data/{lib → mustermann/lib}/mustermann.rb +0 -0
  68. data/{lib → mustermann/lib}/mustermann/ast/boundaries.rb +0 -0
  69. data/{lib → mustermann/lib}/mustermann/ast/compiler.rb +0 -0
  70. data/{lib → mustermann/lib}/mustermann/ast/expander.rb +0 -0
  71. data/{lib → mustermann/lib}/mustermann/ast/node.rb +0 -0
  72. data/{lib → mustermann/lib}/mustermann/ast/param_scanner.rb +0 -0
  73. data/{lib → mustermann/lib}/mustermann/ast/parser.rb +0 -0
  74. data/{lib → mustermann/lib}/mustermann/ast/pattern.rb +0 -0
  75. data/{lib → mustermann/lib}/mustermann/ast/template_generator.rb +0 -0
  76. data/{lib → mustermann/lib}/mustermann/ast/transformer.rb +0 -0
  77. data/{lib → mustermann/lib}/mustermann/ast/translator.rb +0 -0
  78. data/{lib → mustermann/lib}/mustermann/ast/validation.rb +0 -0
  79. data/{lib → mustermann/lib}/mustermann/caster.rb +0 -0
  80. data/{lib → mustermann/lib}/mustermann/composite.rb +0 -0
  81. data/{lib → mustermann/lib}/mustermann/concat.rb +13 -2
  82. data/{lib → mustermann/lib}/mustermann/equality_map.rb +0 -0
  83. data/{lib → mustermann/lib}/mustermann/error.rb +0 -0
  84. data/{lib → mustermann/lib}/mustermann/expander.rb +0 -0
  85. data/{lib → mustermann/lib}/mustermann/extension.rb +0 -0
  86. data/{lib → mustermann/lib}/mustermann/identity.rb +0 -0
  87. data/{lib → mustermann/lib}/mustermann/mapper.rb +0 -0
  88. data/{lib → mustermann/lib}/mustermann/pattern.rb +1 -1
  89. data/{lib → mustermann/lib}/mustermann/pattern_cache.rb +0 -0
  90. data/{lib → mustermann/lib}/mustermann/regexp.rb +0 -0
  91. data/{lib → mustermann/lib}/mustermann/regexp_based.rb +0 -0
  92. data/{lib → mustermann/lib}/mustermann/regular.rb +0 -0
  93. data/{lib → mustermann/lib}/mustermann/simple_match.rb +0 -0
  94. data/{lib → mustermann/lib}/mustermann/sinatra.rb +1 -1
  95. data/{lib → mustermann/lib}/mustermann/sinatra/parser.rb +0 -0
  96. data/{lib → mustermann/lib}/mustermann/sinatra/safe_renderer.rb +0 -0
  97. data/{lib → mustermann/lib}/mustermann/sinatra/try_convert.rb +0 -0
  98. data/{lib → mustermann/lib}/mustermann/to_pattern.rb +0 -0
  99. data/{lib → mustermann/lib}/mustermann/version.rb +1 -1
  100. data/{mustermann.gemspec → mustermann/mustermann.gemspec} +0 -0
  101. data/{spec → mustermann/spec}/ast_spec.rb +0 -0
  102. data/{spec → mustermann/spec}/composite_spec.rb +0 -0
  103. data/{spec → mustermann/spec}/concat_spec.rb +12 -0
  104. data/{spec → mustermann/spec}/equality_map_spec.rb +0 -0
  105. data/{spec → mustermann/spec}/expander_spec.rb +0 -0
  106. data/{spec → mustermann/spec}/extension_spec.rb +0 -0
  107. data/{spec → mustermann/spec}/identity_spec.rb +0 -0
  108. data/{spec → mustermann/spec}/mapper_spec.rb +0 -0
  109. data/{spec → mustermann/spec}/mustermann_spec.rb +0 -0
  110. data/{spec → mustermann/spec}/pattern_spec.rb +0 -0
  111. data/{spec → mustermann/spec}/regexp_based_spec.rb +0 -0
  112. data/{spec → mustermann/spec}/regular_spec.rb +0 -0
  113. data/{spec → mustermann/spec}/simple_match_spec.rb +0 -0
  114. data/{spec → mustermann/spec}/sinatra_spec.rb +0 -0
  115. data/{spec → mustermann/spec}/to_pattern_spec.rb +0 -0
  116. data/support/lib/support.rb +7 -0
  117. data/support/lib/support/coverage.rb +23 -0
  118. data/support/lib/support/env.rb +19 -0
  119. data/support/lib/support/expand_matcher.rb +28 -0
  120. data/support/lib/support/generate_template_matcher.rb +27 -0
  121. data/support/lib/support/match_matcher.rb +39 -0
  122. data/support/lib/support/pattern.rb +42 -0
  123. data/support/lib/support/projects.rb +20 -0
  124. data/support/lib/support/scan_matcher.rb +63 -0
  125. data/support/support.gemspec +27 -0
  126. metadata +128 -58
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+ require 'mustermann/visualizer/tree'
3
+ require 'mustermann/ast/translator'
4
+ require 'hansi'
5
+
6
+ module Mustermann
7
+ module Visualizer
8
+ # Turns an AST into a Tree
9
+ # @!visibility private
10
+ class TreeRenderer < AST::Translator
11
+ TEMPLATE = '"<base01>%s</base01><underline><green>%s</green></underline><base01>%s</base01>" '
12
+ THEME = Hansi::Theme[:solarized]
13
+ PREFIX_COLOR = THEME[:violet]
14
+ FakeNode = Struct.new(:type, :start, :stop, :length)
15
+ private_constant(:TEMPLATE, :THEME, :PREFIX_COLOR, :FakeNode)
16
+
17
+ # Takes a pattern (or pattern string and option) and turns it into a tree.
18
+ # Runs translation if pattern implements to_ast, otherwise returns single
19
+ # node tree.
20
+ #
21
+ # @!visibility private
22
+ def self.render(pattern, **options)
23
+ pattern &&= Mustermann.new(pattern, **options)
24
+ renderer = new(pattern.to_s)
25
+ if pattern.respond_to? :to_ast
26
+ renderer.translate(pattern.to_ast)
27
+ else
28
+ length = renderer.string.length
29
+ node = FakeNode.new("pattern (not AST based)", 0, length, length)
30
+ renderer.tree(node)
31
+ end
32
+ end
33
+
34
+ # @!visibility private
35
+ attr_reader :string
36
+
37
+ # @!visibility private
38
+ def initialize(string)
39
+ @string = string
40
+ end
41
+
42
+ # access a substring of the pattern, in inspect mode
43
+ # @!visibility private
44
+ def sub(*args)
45
+ string[*args].inspect[1..-2]
46
+ end
47
+
48
+ # creates a tree node
49
+ # @!visibility private
50
+ def tree(node, *children, **typed_children)
51
+ children += children_for(typed_children)
52
+ children = children.flatten.grep(Tree)
53
+ infos = sub(0, node.start), sub(node.start, node.length), sub(node.stop..-1)
54
+ description = Hansi.render(THEME[:green], node.type.to_s.tr("_", " "))
55
+ after = Hansi.render(TEMPLATE, *infos, theme: THEME, tags: true)
56
+ Tree.new(description, *children, after: after, prefix_color: PREFIX_COLOR)
57
+ end
58
+
59
+ # Take a hash with trees as values and turn the keys into trees, too.
60
+ # Read again if that didn't make sense.
61
+ # @!visibility private
62
+ def children_for(list)
63
+ list.map do |key, value|
64
+ value = Array(value).flatten
65
+ if value.any?
66
+ after = " " * string.inspect.length + " "
67
+ description = Hansi.render(THEME[:orange], key.to_s)
68
+ Tree.new(description, *value, after: after, prefix_color: PREFIX_COLOR)
69
+ end
70
+ end
71
+ end
72
+
73
+ translate(:node) { t.tree(node, payload: t(payload)) }
74
+ translate(:with_look_ahead) { t.tree(node, head: t(head), payload: t(payload)) }
75
+ translate(Array) { map { |e| t(e) }}
76
+ translate(Object) { }
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,19 @@
1
+ $:.unshift File.expand_path("../../mustermann/lib", __FILE__)
2
+ require "mustermann/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "mustermann-contrib"
6
+ s.version = Mustermann::VERSION
7
+ s.authors = ["Konstantin Haase", "Zachary Scott"]
8
+ s.email = "sinatrarb@googlegroups.com"
9
+ s.homepage = "https://github.com/sinatra/mustermann"
10
+ s.summary = %q{Collection of extensions for Mustermann}
11
+ s.description = %q{Adds many plugins to Mustermman}
12
+ s.license = 'MIT'
13
+ s.required_ruby_version = '>= 2.2.0'
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.add_dependency 'mustermann', Mustermann::VERSION
18
+ s.add_dependency 'hansi', '~> 0.2.0'
19
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ require 'support'
3
+ require 'mustermann/cake'
4
+
5
+ describe Mustermann::Cake do
6
+ extend Support::Pattern
7
+
8
+ pattern '' do
9
+ it { should match('') }
10
+ it { should_not match('/') }
11
+
12
+ it { should expand.to('') }
13
+ it { should_not expand(a: 1) }
14
+
15
+ it { should generate_template('') }
16
+
17
+ it { should respond_to(:expand) }
18
+ it { should respond_to(:to_templates) }
19
+ end
20
+
21
+ pattern '/' do
22
+ it { should match('/') }
23
+ it { should_not match('/foo') }
24
+
25
+ it { should expand.to('/') }
26
+ it { should_not expand(a: 1) }
27
+ end
28
+
29
+ pattern '/foo' do
30
+ it { should match('/foo') }
31
+ it { should_not match('/bar') }
32
+ it { should_not match('/foo.bar') }
33
+
34
+ it { should expand.to('/foo') }
35
+ it { should_not expand(a: 1) }
36
+ end
37
+
38
+ pattern '/foo/bar' do
39
+ it { should match('/foo/bar') }
40
+ it { should_not match('/foo%2Fbar') }
41
+ it { should_not match('/foo%2fbar') }
42
+
43
+ it { should expand.to('/foo/bar') }
44
+ it { should_not expand(a: 1) }
45
+ end
46
+
47
+ pattern '/:foo' do
48
+ it { should match('/foo') .capturing foo: 'foo' }
49
+ it { should match('/bar') .capturing foo: 'bar' }
50
+ it { should match('/foo.bar') .capturing foo: 'foo.bar' }
51
+ it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
52
+ it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
53
+
54
+ it { should_not match('/foo?') }
55
+ it { should_not match('/foo/bar') }
56
+ it { should_not match('/') }
57
+ it { should_not match('/foo/') }
58
+
59
+ example { pattern.params('/foo') .should be == {"foo" => "foo"} }
60
+ example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
61
+ example { pattern.params('').should be_nil }
62
+
63
+ it { should expand(foo: 'bar') .to('/bar') }
64
+ it { should expand(foo: 'b r') .to('/b%20r') }
65
+ it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
66
+
67
+ it { should_not expand(foo: 'foo', bar: 'bar') }
68
+ it { should_not expand(bar: 'bar') }
69
+ it { should_not expand }
70
+
71
+ it { should generate_template('/{foo}') }
72
+ end
73
+
74
+ pattern '/*' do
75
+ it { should match('/') }
76
+ it { should match('/foo') }
77
+ it { should match('/foo/bar') }
78
+
79
+ example { pattern.params('/foo/bar') .should be == {"splat" => ["foo", "bar"]}}
80
+ it { should generate_template('/{+splat}') }
81
+ end
82
+
83
+ pattern '/**' do
84
+ it { should match('/') .capturing splat: '' }
85
+ it { should match('/foo') .capturing splat: 'foo' }
86
+ it { should match('/foo/bar') .capturing splat: 'foo/bar' }
87
+
88
+ example { pattern.params('/foo/bar') .should be == {"splat" => ["foo/bar"]} }
89
+ it { should generate_template('/{+splat}') }
90
+ end
91
+ end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+ require 'support'
3
+ require 'mustermann/express'
4
+
5
+ describe Mustermann::Express do
6
+ extend Support::Pattern
7
+
8
+ pattern '' do
9
+ it { should match('') }
10
+ it { should_not match('/') }
11
+
12
+ it { should expand.to('') }
13
+ it { should_not expand(a: 1) }
14
+
15
+ it { should generate_template('') }
16
+
17
+ it { should respond_to(:expand) }
18
+ it { should respond_to(:to_templates) }
19
+ end
20
+
21
+ pattern '/' do
22
+ it { should match('/') }
23
+ it { should_not match('/foo') }
24
+
25
+ it { should expand.to('/') }
26
+ it { should_not expand(a: 1) }
27
+ end
28
+
29
+ pattern '/foo' do
30
+ it { should match('/foo') }
31
+ it { should_not match('/bar') }
32
+ it { should_not match('/foo.bar') }
33
+
34
+ it { should expand.to('/foo') }
35
+ it { should_not expand(a: 1) }
36
+ end
37
+
38
+ pattern '/foo/bar' do
39
+ it { should match('/foo/bar') }
40
+ it { should_not match('/foo%2Fbar') }
41
+ it { should_not match('/foo%2fbar') }
42
+
43
+ it { should expand.to('/foo/bar') }
44
+ it { should_not expand(a: 1) }
45
+ end
46
+
47
+ pattern '/:foo' do
48
+ it { should match('/foo') .capturing foo: 'foo' }
49
+ it { should match('/bar') .capturing foo: 'bar' }
50
+ it { should match('/foo.bar') .capturing foo: 'foo.bar' }
51
+ it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
52
+ it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
53
+
54
+ it { should_not match('/foo?') }
55
+ it { should_not match('/foo/bar') }
56
+ it { should_not match('/') }
57
+ it { should_not match('/foo/') }
58
+
59
+ example { pattern.params('/foo') .should be == {"foo" => "foo"} }
60
+ example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
61
+ example { pattern.params('').should be_nil }
62
+
63
+ it { should expand(foo: 'bar') .to('/bar') }
64
+ it { should expand(foo: 'b r') .to('/b%20r') }
65
+ it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
66
+
67
+ it { should_not expand(foo: 'foo', bar: 'bar') }
68
+ it { should_not expand(bar: 'bar') }
69
+ it { should_not expand }
70
+
71
+ it { should generate_template('/{foo}') }
72
+ end
73
+
74
+ pattern '/:foo+' do
75
+ it { should_not match('/') }
76
+ it { should match('/foo') .capturing foo: 'foo' }
77
+ it { should match('/foo/bar') .capturing foo: 'foo/bar' }
78
+
79
+ it { should expand .to('/') }
80
+ it { should expand(foo: nil) .to('/') }
81
+ it { should expand(foo: '') .to('/') }
82
+ it { should expand(foo: 'foo') .to('/foo') }
83
+ it { should expand(foo: 'foo/bar') .to('/foo/bar') }
84
+ it { should expand(foo: 'foo.bar') .to('/foo.bar') }
85
+
86
+ it { should generate_template('/{+foo}') }
87
+ end
88
+
89
+ pattern '/:foo?' do
90
+ it { should match('/foo') .capturing foo: 'foo' }
91
+ it { should match('/bar') .capturing foo: 'bar' }
92
+ it { should match('/foo.bar') .capturing foo: 'foo.bar' }
93
+ it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
94
+ it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
95
+ it { should match('/') }
96
+
97
+ it { should_not match('/foo?') }
98
+ it { should_not match('/foo/bar') }
99
+ it { should_not match('/foo/') }
100
+
101
+ example { pattern.params('/foo') .should be == {"foo" => "foo"} }
102
+ example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
103
+ example { pattern.params('/') .should be == {"foo" => nil } }
104
+
105
+ it { should expand(foo: 'bar') .to('/bar') }
106
+ it { should expand(foo: 'b r') .to('/b%20r') }
107
+ it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
108
+ it { should expand .to('/') }
109
+
110
+ it { should_not expand(foo: 'foo', bar: 'bar') }
111
+ it { should_not expand(bar: 'bar') }
112
+
113
+ it { should generate_template('/{foo}') }
114
+ it { should generate_template('/') }
115
+ end
116
+
117
+ pattern '/:foo*' do
118
+ it { should match('/') .capturing foo: '' }
119
+ it { should match('/foo') .capturing foo: 'foo' }
120
+ it { should match('/foo/bar') .capturing foo: 'foo/bar' }
121
+
122
+ it { should expand .to('/') }
123
+ it { should expand(foo: nil) .to('/') }
124
+ it { should expand(foo: '') .to('/') }
125
+ it { should expand(foo: 'foo') .to('/foo') }
126
+ it { should expand(foo: 'foo/bar') .to('/foo/bar') }
127
+ it { should expand(foo: 'foo.bar') .to('/foo.bar') }
128
+
129
+ it { should generate_template('/{+foo}') }
130
+ end
131
+
132
+ pattern '/:foo(.*)' do
133
+ it { should match('/') .capturing foo: '' }
134
+ it { should match('/foo') .capturing foo: 'foo' }
135
+ it { should match('/foo/bar') .capturing foo: 'foo/bar' }
136
+
137
+ it { should expand(foo: '') .to('/') }
138
+ it { should expand(foo: 'foo') .to('/foo') }
139
+ it { should expand(foo: 'foo/bar') .to('/foo/bar') }
140
+ it { should expand(foo: 'foo.bar') .to('/foo.bar') }
141
+
142
+ it { should generate_template('/{foo}') }
143
+ end
144
+
145
+ pattern '/:foo(\d+)' do
146
+ it { should_not match('/') }
147
+ it { should_not match('/foo') }
148
+ it { should match('/15') .capturing foo: '15' }
149
+ it { should generate_template('/{foo}') }
150
+ end
151
+
152
+ pattern '/:foo(\d+|bar)' do
153
+ it { should_not match('/') }
154
+ it { should_not match('/foo') }
155
+ it { should match('/15') .capturing foo: '15' }
156
+ it { should match('/bar') .capturing foo: 'bar' }
157
+ it { should generate_template('/{foo}') }
158
+ end
159
+
160
+ pattern '/:foo(\))' do
161
+ it { should_not match('/') }
162
+ it { should_not match('/foo') }
163
+ it { should match('/)').capturing foo: ')' }
164
+ it { should generate_template('/{foo}') }
165
+ end
166
+
167
+ pattern '/:foo(prefix(\d+|bar))' do
168
+ it { should_not match('/prefix') }
169
+ it { should_not match('/prefixfoo') }
170
+ it { should match('/prefix15') .capturing foo: 'prefix15' }
171
+ it { should match('/prefixbar') .capturing foo: 'prefixbar' }
172
+ it { should generate_template('/{foo}') }
173
+ end
174
+
175
+ pattern '/(.+)' do
176
+ it { should_not match('/') }
177
+ it { should match('/foo') .capturing splat: 'foo' }
178
+ it { should match('/foo/bar') .capturing splat: 'foo/bar' }
179
+ it { should generate_template('/{+splat}') }
180
+ end
181
+
182
+ pattern '/(foo(a|b))' do
183
+ it { should_not match('/') }
184
+ it { should match('/fooa') .capturing splat: 'fooa' }
185
+ it { should match('/foob') .capturing splat: 'foob' }
186
+ it { should generate_template('/{+splat}') }
187
+ end
188
+
189
+ context 'invalid syntax' do
190
+ example 'unexpected closing parenthesis' do
191
+ expect { Mustermann::Express.new('foo)bar') }.
192
+ to raise_error(Mustermann::ParseError, 'unexpected ) while parsing "foo)bar"')
193
+ end
194
+
195
+ example 'missing closing parenthesis' do
196
+ expect { Mustermann::Express.new('foo(bar') }.
197
+ to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo(bar"')
198
+ end
199
+
200
+ example 'unexpected ?' do
201
+ expect { Mustermann::Express.new('foo?bar') }.
202
+ to raise_error(Mustermann::ParseError, 'unexpected ? while parsing "foo?bar"')
203
+ end
204
+
205
+ example 'unexpected *' do
206
+ expect { Mustermann::Express.new('foo*bar') }.
207
+ to raise_error(Mustermann::ParseError, 'unexpected * while parsing "foo*bar"')
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+ require 'support'
3
+ require 'mustermann/file_utils'
4
+
5
+ describe Mustermann::FileUtils do
6
+ subject(:utils) { Mustermann::FileUtils }
7
+ include FileUtils
8
+
9
+ before do
10
+ @pwd = Dir.pwd
11
+ @tmp_dir = File.join(__dir__, 'tmp')
12
+
13
+ mkdir_p(@tmp_dir)
14
+ chdir(@tmp_dir)
15
+
16
+ touch("foo.txt")
17
+ touch("foo.rb")
18
+ touch("bar.txt")
19
+ touch("bar.rb")
20
+ end
21
+
22
+ after do
23
+ chdir(@pwd) if @pwd
24
+ rm_rf(@tmp_dir) if @tmp_dir
25
+ end
26
+
27
+ describe :glob_pattern do
28
+ example { utils.glob_pattern('/:foo') .should be == '/*' }
29
+ example { utils.glob_pattern('/*foo') .should be == '/**/*' }
30
+ example { utils.glob_pattern('/(ab|c)?/:foo/d/*bar') .should be == '/{{ab,c},}/*/d/**/*' }
31
+ example { utils.glob_pattern('/a', '/b') .should be == '{/a,/b}' }
32
+ example { utils.glob_pattern('**/*', type: :shell) .should be == '**/*' }
33
+ example { utils.glob_pattern(/can't parse this/) .should be == '**/*' }
34
+ example { utils.glob_pattern('/foo', type: :identity) .should be == '/foo' }
35
+ example { utils.glob_pattern('/fo*', type: :identity) .should be == '/fo\\*' }
36
+ end
37
+
38
+ describe :glob do
39
+ example do
40
+ utils.glob(":name.txt").sort.should be == ['bar.txt', 'foo.txt']
41
+ end
42
+
43
+ example do
44
+ extensions = []
45
+ utils.glob("foo.:ext") { |file, params| extensions << params['ext'] }
46
+ extensions.sort.should be == ['rb', 'txt']
47
+ end
48
+
49
+ example do
50
+ utils.glob(":name.:ext", capture: { ext: 'rb', name: 'foo' }).should be == ['foo.rb']
51
+ end
52
+ end
53
+
54
+ describe :glob_map do
55
+ example do
56
+ utils.glob_map(':name.rb' => ':name/init.rb').should be == {
57
+ "bar.rb" => "bar/init.rb",
58
+ "foo.rb" => "foo/init.rb"
59
+ }
60
+ end
61
+
62
+ example do
63
+ result = {}
64
+ returned = utils.glob_map(':name.rb' => ':name/init.rb') { |k, v| result[v] = k.upcase }
65
+ returned.sort .should be == ["BAR.RB", "FOO.RB"]
66
+ result["bar/init.rb"] .should be == "BAR.RB"
67
+ end
68
+ end
69
+
70
+ describe :cp do
71
+ example do
72
+ utils.cp(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
73
+ File.should be_exist('foo.ruby')
74
+ File.should be_exist('bar.md')
75
+ File.should be_exist('bar.txt')
76
+ end
77
+ end
78
+
79
+ describe :cp_r do
80
+ example do
81
+ mkdir_p "foo/bar"
82
+ utils.cp_r('foo/:name' => :name)
83
+ File.should be_directory('bar')
84
+ end
85
+ end
86
+
87
+ describe :mv do
88
+ example do
89
+ utils.mv(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
90
+ File.should be_exist('foo.ruby')
91
+ File.should be_exist('bar.md')
92
+ File.should_not be_exist('bar.txt')
93
+ end
94
+ end
95
+
96
+ describe :ln do
97
+ example do
98
+ utils.ln(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
99
+ File.should be_exist('foo.ruby')
100
+ File.should be_exist('bar.md')
101
+ File.should be_exist('bar.txt')
102
+ end
103
+ end
104
+
105
+ describe :ln_s do
106
+ example do
107
+ utils.ln_s(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
108
+ File.should be_symlink('foo.ruby')
109
+ File.should be_symlink('bar.md')
110
+ File.should be_exist('bar.txt')
111
+ end
112
+ end
113
+
114
+ describe :ln_sf do
115
+ example do
116
+ utils.ln_sf(':name.rb' => ':name.txt')
117
+ File.should be_symlink('foo.txt')
118
+ end
119
+ end
120
+ end