mustermann 1.0.2.rc1 → 1.0.2.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +799 -230
  3. data/{mustermann/bench → bench}/capturing.rb +0 -0
  4. data/{mustermann/bench → bench}/regexp.rb +0 -0
  5. data/{mustermann/bench → bench}/simple_vs_sinatra.rb +0 -0
  6. data/{mustermann/bench → bench}/template_vs_addressable.rb +0 -0
  7. data/{mustermann/lib → lib}/mustermann.rb +0 -0
  8. data/{mustermann/lib → lib}/mustermann/ast/boundaries.rb +0 -0
  9. data/{mustermann/lib → lib}/mustermann/ast/compiler.rb +0 -0
  10. data/{mustermann/lib → lib}/mustermann/ast/expander.rb +0 -0
  11. data/{mustermann/lib → lib}/mustermann/ast/node.rb +0 -0
  12. data/{mustermann/lib → lib}/mustermann/ast/param_scanner.rb +0 -0
  13. data/{mustermann/lib → lib}/mustermann/ast/parser.rb +0 -0
  14. data/{mustermann/lib → lib}/mustermann/ast/pattern.rb +0 -0
  15. data/{mustermann/lib → lib}/mustermann/ast/template_generator.rb +0 -0
  16. data/{mustermann/lib → lib}/mustermann/ast/transformer.rb +0 -0
  17. data/{mustermann/lib → lib}/mustermann/ast/translator.rb +0 -0
  18. data/{mustermann/lib → lib}/mustermann/ast/validation.rb +0 -0
  19. data/{mustermann/lib → lib}/mustermann/caster.rb +0 -0
  20. data/{mustermann/lib → lib}/mustermann/composite.rb +0 -0
  21. data/{mustermann/lib → lib}/mustermann/concat.rb +0 -0
  22. data/{mustermann/lib → lib}/mustermann/equality_map.rb +0 -0
  23. data/{mustermann/lib → lib}/mustermann/error.rb +0 -0
  24. data/{mustermann/lib → lib}/mustermann/expander.rb +0 -0
  25. data/{mustermann/lib → lib}/mustermann/extension.rb +0 -0
  26. data/{mustermann/lib → lib}/mustermann/identity.rb +0 -0
  27. data/{mustermann/lib → lib}/mustermann/mapper.rb +0 -0
  28. data/{mustermann/lib → lib}/mustermann/pattern.rb +0 -0
  29. data/{mustermann/lib → lib}/mustermann/pattern_cache.rb +0 -0
  30. data/{mustermann/lib → lib}/mustermann/regexp.rb +0 -0
  31. data/{mustermann/lib → lib}/mustermann/regexp_based.rb +0 -0
  32. data/{mustermann/lib → lib}/mustermann/regular.rb +0 -0
  33. data/{mustermann/lib → lib}/mustermann/simple_match.rb +0 -0
  34. data/{mustermann/lib → lib}/mustermann/sinatra.rb +0 -0
  35. data/{mustermann/lib → lib}/mustermann/sinatra/parser.rb +0 -0
  36. data/{mustermann/lib → lib}/mustermann/sinatra/safe_renderer.rb +0 -0
  37. data/{mustermann/lib → lib}/mustermann/sinatra/try_convert.rb +0 -0
  38. data/{mustermann/lib → lib}/mustermann/to_pattern.rb +0 -0
  39. data/{mustermann/lib → lib}/mustermann/version.rb +1 -1
  40. data/{mustermann/mustermann.gemspec → mustermann.gemspec} +0 -0
  41. data/{mustermann/spec → spec}/ast_spec.rb +0 -0
  42. data/{mustermann/spec → spec}/composite_spec.rb +0 -0
  43. data/{mustermann/spec → spec}/concat_spec.rb +0 -0
  44. data/{mustermann/spec → spec}/equality_map_spec.rb +0 -0
  45. data/{mustermann/spec → spec}/expander_spec.rb +0 -0
  46. data/{mustermann/spec → spec}/extension_spec.rb +0 -0
  47. data/{mustermann/spec → spec}/identity_spec.rb +0 -0
  48. data/{mustermann/spec → spec}/mapper_spec.rb +0 -0
  49. data/{mustermann/spec → spec}/mustermann_spec.rb +0 -0
  50. data/{mustermann/spec → spec}/pattern_spec.rb +0 -0
  51. data/{mustermann/spec → spec}/regexp_based_spec.rb +0 -0
  52. data/{mustermann/spec → spec}/regular_spec.rb +0 -0
  53. data/{mustermann/spec → spec}/simple_match_spec.rb +0 -0
  54. data/{mustermann/spec → spec}/sinatra_spec.rb +0 -0
  55. data/{mustermann/spec → spec}/to_pattern_spec.rb +0 -0
  56. metadata +71 -126
  57. data/.gitignore +0 -18
  58. data/.rspec +0 -5
  59. data/.travis.yml +0 -25
  60. data/.yardopts +0 -3
  61. data/Gemfile +0 -7
  62. data/Rakefile +0 -27
  63. data/mustermann-contrib/LICENSE +0 -23
  64. data/mustermann-contrib/README.md +0 -1155
  65. data/mustermann-contrib/examples/highlighting.rb +0 -35
  66. data/mustermann-contrib/highlighting.png +0 -0
  67. data/mustermann-contrib/irb.png +0 -0
  68. data/mustermann-contrib/lib/mustermann/cake.rb +0 -19
  69. data/mustermann-contrib/lib/mustermann/express.rb +0 -38
  70. data/mustermann-contrib/lib/mustermann/file_utils.rb +0 -218
  71. data/mustermann-contrib/lib/mustermann/file_utils/glob_pattern.rb +0 -40
  72. data/mustermann-contrib/lib/mustermann/fileutils.rb +0 -1
  73. data/mustermann-contrib/lib/mustermann/flask.rb +0 -199
  74. data/mustermann-contrib/lib/mustermann/pyramid.rb +0 -29
  75. data/mustermann-contrib/lib/mustermann/rails.rb +0 -47
  76. data/mustermann-contrib/lib/mustermann/shell.rb +0 -57
  77. data/mustermann-contrib/lib/mustermann/simple.rb +0 -51
  78. data/mustermann-contrib/lib/mustermann/string_scanner.rb +0 -314
  79. data/mustermann-contrib/lib/mustermann/strscan.rb +0 -1
  80. data/mustermann-contrib/lib/mustermann/template.rb +0 -63
  81. data/mustermann-contrib/lib/mustermann/uri_template.rb +0 -1
  82. data/mustermann-contrib/lib/mustermann/versions.rb +0 -47
  83. data/mustermann-contrib/lib/mustermann/visualizer.rb +0 -39
  84. data/mustermann-contrib/lib/mustermann/visualizer/highlight.rb +0 -138
  85. data/mustermann-contrib/lib/mustermann/visualizer/highlighter.rb +0 -38
  86. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/ad_hoc.rb +0 -95
  87. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/ast.rb +0 -103
  88. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/composite.rb +0 -46
  89. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/dummy.rb +0 -19
  90. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/regular.rb +0 -105
  91. data/mustermann-contrib/lib/mustermann/visualizer/pattern_extension.rb +0 -69
  92. data/mustermann-contrib/lib/mustermann/visualizer/renderer/ansi.rb +0 -24
  93. data/mustermann-contrib/lib/mustermann/visualizer/renderer/generic.rb +0 -47
  94. data/mustermann-contrib/lib/mustermann/visualizer/renderer/hansi_template.rb +0 -35
  95. data/mustermann-contrib/lib/mustermann/visualizer/renderer/html.rb +0 -51
  96. data/mustermann-contrib/lib/mustermann/visualizer/renderer/sexp.rb +0 -38
  97. data/mustermann-contrib/lib/mustermann/visualizer/tree.rb +0 -64
  98. data/mustermann-contrib/lib/mustermann/visualizer/tree_renderer.rb +0 -79
  99. data/mustermann-contrib/mustermann-contrib.gemspec +0 -19
  100. data/mustermann-contrib/spec/cake_spec.rb +0 -91
  101. data/mustermann-contrib/spec/express_spec.rb +0 -210
  102. data/mustermann-contrib/spec/file_utils_spec.rb +0 -120
  103. data/mustermann-contrib/spec/flask_spec.rb +0 -362
  104. data/mustermann-contrib/spec/flask_subclass_spec.rb +0 -369
  105. data/mustermann-contrib/spec/pattern_extension_spec.rb +0 -50
  106. data/mustermann-contrib/spec/pyramid_spec.rb +0 -102
  107. data/mustermann-contrib/spec/rails_spec.rb +0 -648
  108. data/mustermann-contrib/spec/shell_spec.rb +0 -148
  109. data/mustermann-contrib/spec/simple_spec.rb +0 -269
  110. data/mustermann-contrib/spec/string_scanner_spec.rb +0 -272
  111. data/mustermann-contrib/spec/template_spec.rb +0 -842
  112. data/mustermann-contrib/spec/visualizer_spec.rb +0 -199
  113. data/mustermann-contrib/theme.png +0 -0
  114. data/mustermann-contrib/tree.png +0 -0
  115. data/mustermann/LICENSE +0 -23
  116. data/mustermann/README.md +0 -853
  117. data/support/lib/support.rb +0 -7
  118. data/support/lib/support/coverage.rb +0 -23
  119. data/support/lib/support/env.rb +0 -19
  120. data/support/lib/support/expand_matcher.rb +0 -28
  121. data/support/lib/support/generate_template_matcher.rb +0 -27
  122. data/support/lib/support/match_matcher.rb +0 -39
  123. data/support/lib/support/pattern.rb +0 -42
  124. data/support/lib/support/projects.rb +0 -20
  125. data/support/lib/support/scan_matcher.rb +0 -63
  126. data/support/support.gemspec +0 -27
@@ -1,35 +0,0 @@
1
- require 'bundler/setup'
2
- require 'mustermann/visualizer'
3
-
4
- Hansi.mode = ARGV[0].to_i if ARGV.any?
5
-
6
- def self.example(type, *patterns)
7
- print Hansi.render(:bold, " #{type}: ".ljust(14))
8
- patterns.each do |pattern|
9
- pattern = Mustermann.new(pattern, type: type)
10
- space_after = pattern.to_s.size > 24 ? " " : " " * (25 - pattern.to_s.size)
11
- highlight = Mustermann::Visualizer.highlight(pattern, inspect: true)
12
- print highlight.to_ansi + space_after
13
- end
14
- puts
15
- end
16
-
17
- puts
18
- example(:cake, '/:prefix/**')
19
- example(:express, '/:prefix+/:id(\d+)', '/:page/:slug+')
20
- example(:flask, '/<prefix>/<int:id>', '/user/<int(min=0):id>')
21
- example(:identity, '/image.png')
22
- example(:pyramid, '/{prefix:.*}/{id}', '/{page}/*slug')
23
- example(:rails, '/:slug(.:ext)')
24
- example(:regexp, '/(?<slug>[^/]+)', '/(?:page|user)/(\d+)')
25
- example(:shell, '/**/*', '/\{a,b\}/{a,b}')
26
- example(:simple, '/:page/*slug')
27
- example(:sinatra, '/:page/*slug', '/users/{id}?')
28
- example(:template, '/{+pre}/{page}{?q,p}', '/users/{id}?')
29
- puts
30
-
31
- example(:composition)
32
- composite = Mustermann.new("/{a}", "/{b}/{c}")
33
- puts " " + composite.to_ansi
34
- puts " " + (Mustermann.new("/") ^ composite).to_ansi
35
- puts
Binary file
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/ast/pattern'
4
-
5
- module Mustermann
6
- # CakePHP style pattern implementation.
7
- #
8
- # @example
9
- # Mustermann.new('/:foo', type: :cake) === '/bar' # => true
10
- #
11
- # @see Mustermann::Pattern
12
- # @see file:README.md#cake Syntax description in the README
13
- class Cake < AST::Pattern
14
- register :cake
15
-
16
- on(?:) { |c| node(:capture) { scan(/\w+/) } }
17
- on(?*) { |c| node(:splat, convert: (-> e { e.split('/') } unless scan(?*))) }
18
- end
19
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/ast/pattern'
4
-
5
- module Mustermann
6
- # Express style pattern implementation.
7
- #
8
- # @example
9
- # Mustermann.new('/:foo', type: :express) === '/bar' # => true
10
- #
11
- # @see Mustermann::Pattern
12
- # @see file:README.md#flask Syntax description in the README
13
- class Express < AST::Pattern
14
- register :express
15
-
16
- on(nil, ??, ?+, ?*, ?)) { |c| unexpected(c) }
17
- on(?:) { |c| node(:capture) { scan(/\w+/) } }
18
- on(?() { |c| node(:splat, constraint: read_brackets(?(, ?))) }
19
-
20
- suffix ??, after: :capture do |char, element|
21
- unexpected(char) unless element.is_a? :capture
22
- node(:optional, element)
23
- end
24
-
25
- suffix ?*, after: :capture do |match, element|
26
- node(:named_splat, element.name)
27
- end
28
-
29
- suffix ?+, after: :capture do |match, element|
30
- node(:named_splat, element.name, constraint: ".+")
31
- end
32
-
33
- suffix ?(, after: :capture do |match, element|
34
- element.constraint = read_brackets(?(, ?))
35
- element
36
- end
37
- end
38
- end
@@ -1,218 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/file_utils/glob_pattern'
4
- require 'mustermann/mapper'
5
- require 'fileutils'
6
-
7
- module Mustermann
8
- # Implements handy file operations using patterns.
9
- module FileUtils
10
- extend self
11
-
12
- # Turn a Mustermann pattern into glob pattern.
13
- #
14
- # @example
15
- # require 'mustermann/file_utils'
16
- #
17
- # Mustermann::FileUtils.glob_pattern('/:name') # => '/*'
18
- # Mustermann::FileUtils.glob_pattern('src/:path/:file.(js|rb)') # => 'src/**/*/*.{js,rb}'
19
- # Mustermann::FileUtils.glob_pattern('{a,b}/*', type: :shell) # => '{a,b}/*'
20
- #
21
- # pattern = Mustermann.new('/foo/:page', '/bar/:page') # => #<Mustermann::Composite:...>
22
- # Mustermann::FileUtils.glob_pattern(pattern) # => "{/foo/*,/bar/*}"
23
- #
24
- # @param [Object] pattern the object to turn into a glob pattern.
25
- # @return [String] the glob pattern
26
- def glob_pattern(*pattern, **options)
27
- pattern_with_glob_pattern(*pattern, **options).last
28
- end
29
-
30
- # Uses the given pattern(s) to search for files and directories.
31
- #
32
- # @example
33
- # require 'mustermann/file_utils'
34
- # Mustermann::FileUtils.glob(':base.:ext') # => ['example.txt']
35
- #
36
- # Mustermann::FileUtils.glob(':base.:ext') do |file, params|
37
- # file # => "example.txt"
38
- # params # => {"base"=>"example", "ext"=>"txt"}
39
- # end
40
- def glob(*pattern, **options, &block)
41
- raise ArgumentError, "no pattern given" if pattern.empty?
42
- pattern, glob_pattern = pattern_with_glob_pattern(*pattern, **options)
43
- results = [] unless block
44
- Dir.glob(glob_pattern) do |result|
45
- next unless params = pattern.params(result)
46
- block ? block[result, params] : results << result
47
- end
48
- results
49
- end
50
-
51
- # Allows to search for files an map these onto other strings.
52
- #
53
- # @example
54
- # require 'mustermann/file_utils'
55
- #
56
- # Mustermann::FileUtils.glob_map(':base.:ext' => ':base.bak.:ext') # => {'example.txt' => 'example.bak.txt'}
57
- # Mustermann::FileUtils.glob_map(':base.:ext' => :base) { |file, mapped| mapped } # => ['example']
58
- #
59
- # @see Mustermann::Mapper
60
- def glob_map(map = {}, **options, &block)
61
- map = Mapper === map ? map : Mapper.new(map, **options)
62
- mapped = glob(*map.to_h.keys).map { |f| [f, unescape(map[f])] }
63
- block ? mapped.map(&block) : Hash[mapped]
64
- end
65
-
66
- # Copies files based on a pattern mapping.
67
- #
68
- # @example
69
- # require 'mustermann/file_utils'
70
- #
71
- # # copies example.txt to example.bak.txt
72
- # Mustermann::FileUtils.cp(':base.:ext' => ':base.bak.:ext')
73
- #
74
- # @see #glob_map
75
- def cp(map = {}, recursive: false, **options)
76
- utils_opts, opts = split_options(:preserve, :dereference_root, :remove_destination, **options)
77
- cp_method = recursive ? :cp_r : :cp
78
- glob_map(map, **opts) { |o,n| f.send(cp_method, o, n, **utils_opts) }
79
- end
80
-
81
-
82
- # Copies files based on a pattern mapping, recursively.
83
- #
84
- # @example
85
- # require 'mustermann/file_utils'
86
- #
87
- # # copies Foo.app/example.txt to Foo.back.app/example.txt
88
- # Mustermann::FileUtils.cp_r(':base.:ext' => ':base.bak.:ext')
89
- #
90
- # @see #glob_map
91
- def cp_r(map = {}, **options)
92
- cp(map, recursive: true, **options)
93
- end
94
-
95
- # Moves files based on a pattern mapping.
96
- #
97
- # @example
98
- # require 'mustermann/file_utils'
99
- #
100
- # # moves example.txt to example.bak.txt
101
- # Mustermann::FileUtils.mv(':base.:ext' => ':base.bak.:ext')
102
- #
103
- # @see #glob_map
104
- def mv(map = {}, **options)
105
- utils_opts, opts = split_options(**options)
106
- glob_map(map, **opts) { |o,n| f.mv(o, n, **utils_opts) }
107
- end
108
-
109
-
110
- # Creates links based on a pattern mapping.
111
- #
112
- # @example
113
- # require 'mustermann/file_utils'
114
- #
115
- # # creates a link from bin/example to lib/example.rb
116
- # Mustermann::FileUtils.ln('lib/:name.rb' => 'bin/:name')
117
- #
118
- # @see #glob_map
119
- def ln(map = {}, symbolic: false, **options)
120
- utils_opts, opts = split_options(**options)
121
- link_method = symbolic ? :ln_s : :ln
122
- glob_map(map, **opts) { |o,n| f.send(link_method, o, n, **utils_opts) }
123
- end
124
-
125
- # Creates symbolic links based on a pattern mapping.
126
- #
127
- # @example
128
- # require 'mustermann/file_utils'
129
- #
130
- # # creates a symbolic link from bin/example to lib/example.rb
131
- # Mustermann::FileUtils.ln_s('lib/:name.rb' => 'bin/:name')
132
- #
133
- # @see #glob_map
134
- def ln_s(map = {}, **options)
135
- ln(map, symbolic: true, **options)
136
- end
137
-
138
- # Creates symbolic links based on a pattern mapping.
139
- # Overrides potentailly existing files.
140
- #
141
- # @example
142
- # require 'mustermann/file_utils'
143
- #
144
- # # creates a symbolic link from bin/example to lib/example.rb
145
- # Mustermann::FileUtils.ln_sf('lib/:name.rb' => 'bin/:name')
146
- #
147
- # @see #glob_map
148
- def ln_sf(map = {}, **options)
149
- ln(map, symbolic: true, force: true, **options)
150
- end
151
-
152
-
153
- # Splits options into those meant for Mustermann and those
154
- # meant for ::FileUtils.
155
- #
156
- # @!visibility private
157
- def split_options(*utils_option_names, **options)
158
- utils_options, pattern_options = {}, {}
159
- utils_option_names += %i[force noop verbose]
160
-
161
- options.each do |key, value|
162
- list = utils_option_names.include?(key) ? utils_options : pattern_options
163
- list[key] = value
164
- end
165
-
166
- [utils_options, pattern_options]
167
- end
168
-
169
- # Create a Mustermann pattern from whatever the input is and turn it into
170
- # a glob pattern.
171
- #
172
- # @!visibility private
173
- def pattern_with_glob_pattern(*pattern, **options)
174
- options[:uri_decode] ||= false
175
- pattern = Mustermann.new(*pattern.flatten, **options)
176
- @glob_patterns ||= {}
177
- @glob_patterns[pattern] ||= GlobPattern.generate(pattern)
178
- [pattern, @glob_patterns[pattern]]
179
- end
180
-
181
- # The FileUtils method to use.
182
- # @!visibility private
183
- def f
184
- ::FileUtils
185
- end
186
-
187
- # Unescape an URI escaped string.
188
- # @!visibility private
189
- def unescape(string)
190
- @uri ||= URI::Parser.new
191
- @uri.unescape(string)
192
- end
193
-
194
- # Create a new version of Mustermann::FileUtils using a different ::FileUtils module.
195
- # @see DryRun
196
- # @!visibility private
197
- def with_file_utils(&block)
198
- Module.new do
199
- include Mustermann::FileUtils
200
- define_method(:f, &block)
201
- private(:f)
202
- extend self
203
- end
204
- end
205
-
206
- private :pattern_with_glob_pattern, :split_options, :f, :unescape
207
-
208
- alias_method :copy, :cp
209
- alias_method :move, :mv
210
- alias_method :link, :ln
211
- alias_method :symlink, :ln_s
212
- alias_method :[], :glob
213
-
214
- DryRun ||= with_file_utils { ::FileUtils::DryRun }
215
- NoWrite ||= with_file_utils { ::FileUtils::NoWrite }
216
- Verbose ||= with_file_utils { ::FileUtils::Verbose }
217
- end
218
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann/ast/translator'
3
-
4
- module Mustermann
5
- module FileUtils
6
- # AST Translator to turn Mustermann patterns into glob patterns.
7
- # @!visibility private
8
- class GlobPattern < Mustermann::AST::Translator
9
- # Character that need to be escaped in glob patterns.
10
- # @!visibility private
11
- ESCAPE = %w([ ] { } * ** \\)
12
-
13
- # Turn a Mustermann pattern into glob pattern.
14
- # @param [#to_glob, #to_ast, Object] pattern the object to turn into a glob pattern.
15
- # @return [String] the glob pattern
16
- # @!visibility private
17
- def self.generate(pattern)
18
- return pattern.to_glob if pattern.respond_to? :to_glob
19
- return new.translate(pattern.to_ast) if pattern.respond_to? :to_ast
20
- return "**/*" unless pattern.is_a? Mustermann::Composite
21
- "{#{pattern.patterns.map { |p| generate(p) }.join(',')}}"
22
- end
23
-
24
- translate(:root, :group, :expression) { t(payload) || "" }
25
- translate(:separator, :char) { t.escape(payload) }
26
- translate(:capture) { constraint ? "**/*" : "*" }
27
- translate(:optional) { "{#{t(payload)},}" }
28
- translate(:named_splat, :splat) { "**/*" }
29
- translate(:with_look_ahead) { t(head) + t(payload) }
30
- translate(:union) { "{#{payload.map { |e| t(e) }.join(',')}}" }
31
- translate(Array) { map { |e| t(e) }.join }
32
-
33
- # Escape with a slash rather than URI escaping.
34
- # @!visibility private
35
- def escape(char)
36
- ESCAPE.include?(char) ? "\\#{char}" : char
37
- end
38
- end
39
- end
40
- end
@@ -1 +0,0 @@
1
- require 'mustermann/file_utils'
@@ -1,199 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/ast/pattern'
4
-
5
- module Mustermann
6
- # Flask style pattern implementation.
7
- #
8
- # @example
9
- # Mustermann.new('/<foo>', type: :flask) === '/bar' # => true
10
- #
11
- # @see Mustermann::Pattern
12
- # @see file:README.md#flask Syntax description in the README
13
- class Flask < AST::Pattern
14
- include Concat::Native
15
- register :flask
16
-
17
- on(nil, ?>, ?:) { |c| unexpected(c) }
18
-
19
- on(?<) do |char|
20
- converter_name = expect(/\w+/, char: char)
21
- args, opts = scan(?() ? read_args(?=, ?)) : [[], {}]
22
-
23
- if scan(?:)
24
- name = read_escaped(?>)
25
- else
26
- converter_name, name = 'default', converter_name
27
- expect(?>)
28
- end
29
-
30
- converter = pattern.converters.fetch(converter_name) { unexpected("converter %p" % converter_name) }
31
- converter = converter.new(*args, **opts) if converter.respond_to? :new
32
- constraint = converter.constraint if converter.respond_to? :constraint
33
- convert = converter.convert if converter.respond_to? :convert
34
- qualifier = converter.qualifier if converter.respond_to? :qualifier
35
- node_type = converter.node_type if converter.respond_to? :node_type
36
- node_type ||= :capture
37
-
38
- node(node_type, name, convert: convert, constraint: constraint, qualifier: qualifier)
39
- end
40
-
41
- # A class for easy creating of converters.
42
- # @see Mustermann::Flask#register_converter
43
- class Converter
44
- # Constraint on the format used for the capture.
45
- # Should be a regexp (or a string corresponding to a regexp)
46
- # @see Mustermann::Flask#register_converter
47
- attr_accessor :constraint
48
-
49
- # Callback
50
- # Should be a Proc.
51
- # @see Mustermann::Flask#register_converter
52
- attr_accessor :convert
53
-
54
- # Constraint on the format used for the capture.
55
- # Should be a regexp (or a string corresponding to a regexp)
56
- # @see Mustermann::Flask#register_converter
57
- # @!visibility private
58
- attr_accessor :node_type
59
-
60
- # Constraint on the format used for the capture.
61
- # Should be a regexp (or a string corresponding to a regexp)
62
- # @see Mustermann::Flask#register_converter
63
- # @!visibility private
64
- attr_accessor :qualifier
65
-
66
- # @!visibility private
67
- def self.create(&block)
68
- Class.new(self) do
69
- define_method(:initialize) { |*a, **o| block[self, *a, **o] }
70
- end
71
- end
72
-
73
- # Makes sure a given value falls inbetween a min and a max.
74
- # Uses the passed block to convert the value from a string to whatever
75
- # format you'd expect.
76
- #
77
- # @example
78
- # require 'mustermann/flask'
79
- #
80
- # class MyPattern < Mustermann::Flask
81
- # register_converter(:x) { between(5, 15, &:to_i) }
82
- # end
83
- #
84
- # pattern = MyPattern.new('<x:id>')
85
- # pattern.params('/12') # => { 'id' => 12 }
86
- # pattern.params('/16') # => { 'id' => 15 }
87
- #
88
- # @see Mustermann::Flask#register_converter
89
- def between(min, max)
90
- self.convert = proc do |input|
91
- value = yield(input)
92
- value = yield(min) if min and value < yield(min)
93
- value = yield(max) if max and value > yield(max)
94
- value
95
- end
96
- end
97
- end
98
-
99
- # Generally available converters.
100
- # @!visibility private
101
- def self.converters(inherited = true)
102
- return @converters ||= {} unless inherited
103
- defaults = superclass.respond_to?(:converters) ? superclass.converters : {}
104
- defaults.merge(converters(false))
105
- end
106
-
107
- # Allows you to register your own converters.
108
- #
109
- # It is reommended to use this on a subclass, so to not influence other subsystems
110
- # using flask templates.
111
- #
112
- # The object passed in as converter can implement #convert and/or #constraint.
113
- #
114
- # It can also instead implement #new, which will then return an object responding
115
- # to some of these methods. Arguments from the flask pattern will be passed to #new.
116
- #
117
- # If passed a block, it will be yielded to with a {Mustermann::Flask::Converter}
118
- # instance and any arguments in the flask pattern.
119
- #
120
- # @example with simple object
121
- # require 'mustermann/flask'
122
- #
123
- # MyPattern = Class.new(Mustermann::Flask)
124
- # up_converter = Struct.new(:convert).new(:upcase.to_proc)
125
- # MyPattern.register_converter(:upper, up_converter)
126
- #
127
- # MyPattern.new("/<up:name>").params('/foo') # => { "name" => "FOO" }
128
- #
129
- # @example with block
130
- # require 'mustermann/flask'
131
- #
132
- # MyPattern = Class.new(Mustermann::Flask)
133
- # MyPattern.register_converter(:upper) { |c| c.convert = :upcase.to_proc }
134
- #
135
- # MyPattern.new("/<up:name>").params('/foo') # => { "name" => "FOO" }
136
- #
137
- # @example with converter class
138
- # require 'mustermann/flasl'
139
- #
140
- # class MyPattern < Mustermann::Flask
141
- # class Converter
142
- # attr_reader :convert
143
- # def initialize(send: :to_s)
144
- # @convert = send.to_sym.to_proc
145
- # end
146
- # end
147
- #
148
- # register_converter(:t, Converter)
149
- # end
150
- #
151
- # MyPattern.new("/<t(send=upcase):name>").params('/Foo') # => { "name" => "FOO" }
152
- # MyPattern.new("/<t(send=downcase):name>").params('/Foo') # => { "name" => "foo" }
153
- #
154
- # @param [#to_s] name converter name
155
- # @param [#new, #convert, #constraint, nil] converter
156
- def self.register_converter(name, converter = nil, &block)
157
- converter ||= Converter.create(&block)
158
- converters(false)[name.to_s] = converter
159
- end
160
-
161
- register_converter(:string) do |converter, minlength: nil, maxlength: nil, length: nil|
162
- converter.qualifier = "{%s,%s}" % [minlength || 1, maxlength] if minlength or maxlength
163
- converter.qualifier = "{%s}" % length if length
164
- end
165
-
166
- register_converter(:int) do |converter, min: nil, max: nil, fixed_digits: false|
167
- converter.constraint = /\d/
168
- converter.qualifier = "{#{fixed_digits}}" if fixed_digits
169
- converter.between(min, max) { |string| Integer(string) }
170
- end
171
-
172
- register_converter(:float) do |converter, min: nil, max: nil|
173
- converter.constraint = /\d*\.?\d+/
174
- converter.qualifier = ""
175
- converter.between(min, max) { |string| Float(string) }
176
- end
177
-
178
- register_converter(:path) do |converter|
179
- converter.node_type = :named_splat
180
- end
181
-
182
- register_converter(:any) do |converter, *strings|
183
- strings = strings.map { |s| Regexp.escape(s) unless s == {} }.compact
184
- converter.qualifier = ""
185
- converter.constraint = Regexp.union(*strings)
186
- end
187
-
188
- register_converter(:default, converters['string'])
189
-
190
- supported_options :converters
191
- attr_reader :converters
192
-
193
- def initialize(input, converters: {}, **options)
194
- @converters = self.class.converters.dup
195
- converters.each { |k,v| @converters[k.to_s] = v } if converters
196
- super(input, **options)
197
- end
198
- end
199
- end