pax_global_header 0000666 0000000 0000000 00000000064 13603656127 0014523 g ustar 00root root 0000000 0000000 52 comment=a085164d64020a273949153999f01e7879885913
mustermann-1.1.1/ 0000775 0000000 0000000 00000000000 13603656127 0013714 5 ustar 00root root 0000000 0000000 mustermann-1.1.1/.gitignore 0000664 0000000 0000000 00000000255 13603656127 0015706 0 ustar 00root root 0000000 0000000 *.gem
*.rbc
.bundle
.config
.yardoc
.test_queue_stats
.coverage
Gemfile.lock
InstalledFiles
_yardoc
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
mustermann-1.1.1/.rspec 0000664 0000000 0000000 00000000107 13603656127 0015027 0 ustar 00root root 0000000 0000000 -P '*/spec/**_spec.rb'
-r bundler/setup
--default-path .
--color
--tty
mustermann-1.1.1/.travis.yml 0000664 0000000 0000000 00000000506 13603656127 0016026 0 ustar 00root root 0000000 0000000 language: ruby
sudo: false
cache: bundler
before_install:
- gem update bundler
rvm:
- 2.2.10
- 2.3.8
- 2.4.5
- 2.5.3
- 2.6.5
- 2.7.0
- ruby-head
matrix:
allow_failures:
- rvm: ruby-head
fast_finish: true
notifications:
recipients:
- mail@zzak.io
slack: sinatrarb:LQGhUfGYcqRgRzwKea0bqUhY
mustermann-1.1.1/.yardopts 0000664 0000000 0000000 00000000071 13603656127 0015560 0 ustar 00root root 0000000 0000000 --charset utf-8
mustermann*/{lib,app}/**/*.rb
ext/**/*.c
mustermann-1.1.1/Gemfile 0000664 0000000 0000000 00000000335 13603656127 0015210 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org'
require File.expand_path('../support/lib/support/projects', __FILE__)
gem 'ruby2_keywords'
path '.' do
Support::Projects.each { |name| gem(name) }
gem 'support', group: :development
end
mustermann-1.1.1/LICENSE 0000664 0000000 0000000 00000002117 13603656127 0014722 0 ustar 00root root 0000000 0000000 Copyright (c) 2013-2017 Konstantin Haase
Copyright (c) 2016-2017 Zachary Scott
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
mustermann-1.1.1/README.md 0000664 0000000 0000000 00000037111 13603656127 0015176 0 ustar 00root root 0000000 0000000 # The Amazing Mustermann
[](https://travis-ci.org/sinatra/mustermann) [](https://coveralls.io/github/rkh/mustermann?branch=master) [](https://codeclimate.com/github/rkh/mustermann) [](https://gemnasium.com/rkh/mustermann) [](https://rubygems.org/gems/mustermann)
[](http://inch-ci.org/github/rkh/mustermann)
[](http://www.rubydoc.info/gems/mustermann/frames)
[](http://rkh.mit-license.org)
[](http://img.shields.io)
This repository contains multiple projects (each installable as separate gems).
* **[mustermann](https://github.com/sinatra/mustermann/blob/master/mustermann/README.md): Your personal string matching expert. This is probably what you're looking for.**
* [mustermann-contrib](https://github.com/sinatra/mustermann/blob/master/mustermann-contrib/README.md): A meta gem depending on all other official mustermann gems.
* [mustermann-fileutils](https://github.com/sinatra/mustermann/blob/master/mustermann-contrib/README.md#-mustermann-fileutils): Efficient file system operations using Mustermann patterns.
* [mustermann-strscan](https://github.com/sinatra/mustermann/blob/master/mustermann-contrib/README.md#-mustermann-strscan): A version of Ruby's [StringScanner](http://ruby-doc.org/stdlib-2.0/libdoc/strscan/rdoc/StringScanner.html) made for pattern objects.
* [mustermann-visualizer](https://github.com/sinatra/mustermann/blob/master/mustermann-contrib/README.md#-mustermann-visualizer): Syntax highlighting and tree visualization for patterns.'
* A selection of pattern types for mustermann, each as their own little library, see [below](#-pattern-types).
## Git versions with Bundler
You can easily use the latest edge version from GitHub of any of these gems via [Bundler](http://bundler.io/):
``` ruby
git 'https://github.com/rkh/mustermann.git' do
gem 'mustermann'
gem 'mustermann-rails'
end
```
## Pattern Types
The `identity`, `regexp` and `sinatra` types are included in the `mustermann` gem, all the other types have their own gems.
Type |
Example |
Compatible with |
Notes |
cake |
/:prefix/** |
CakePHP |
|
express |
/:prefix+/:id(\d+) |
Express,
pillar.js
|
|
flask |
/<prefix>/<int:id> |
Flask,
Werkzeug
|
|
identity |
/image.png |
any software using strings |
Exact string matching (no parameter parsing).
|
pyramid |
/{prefix:.*}/{id} |
Pyramid,
Pylons
|
|
rails |
/:slug(.:ext) |
Ruby on Rails,
Journey,
HTTP Router,
Hanami,
Scalatra (if configured),
NYNY |
|
regexp |
/(?<slug>[^\/]+) |
Oniguruma,
Onigmo,
regular expressions
|
Created when you pass a regexp to Mustermann.new.
Does not support expanding or generating templates.
|
shell |
/*.{png,jpg} |
Unix Shell (bash, zsh) |
Does not support expanding or generating templates. |
simple |
/:slug.:ext |
Sinatra (1.x),
Scalatra,
Dancer,
Finatra,
Spark,
RCRouter,
kick.js
|
Implementation is a direct copy from Sinatra 1.3.
It is the predecessor of sinatra.
Does not support expanding or generating templates.
|
sinatra |
/:slug(.:ext)? |
Sinatra (2.x),
Padrino (>= 0.13.0),
Pendragon,
Angelo
|
This is the default and the only type "invented here".
It is a superset of simple and has a common subset with
template (and others).
|
uri-template |
/{+pre}/{page}{?q} |
RFC 6570,
JSON API,
JSON Home Documents
and many more
|
Standardized URI templates, can be generated from most other types. |
Any software using Mustermann is obviously compatible with at least one of the above.
## Requirements
Mustermann depends on [tool](https://github.com/rkh/tool) (which has been extracted from Mustermann and Sinatra 2.0), and a Ruby 2.2 compatible Ruby implementation.
It is known to work on MRI 2.2.
JRuby will hopefully be supported with the release of **JRuby 9000**.
**Rubinius** is not currently supported. As of Rubinius 2.3.1, a large portion of the specs pass (3870 out of 3943), but certain parts are not working yet.
If you need Ruby 1.9 support, you might be able to use the **unofficial** [mustermann19](https://rubygems.org/gems/mustermann19) gem based on [namusyaka's fork](https://github.com/namusyaka/mustermann19).
## Release History
Mustermann follows [Semantic Versioning 2.0](http://semver.org/). Anything documented in the README or via YARD and not declared private is part of the public API.
### Stable Releases
* **Mustermann 1.1.1** (2020-01-04)
* Make sure that `require`ing ruby2_keywords when needed. Fixes [#102](https://github.com/sinatra/mustermann/issues/103) [@Annih](https://github.com/Annih)
* **Mustermann 1.1.0** (2019-12-30)
* Proper handling of `Mustermann::ExpandError`. Fixes [#88](https://github.com/sinatra/mustermann/issues/88) [@namusyaka](https://github.com/namusyaka)
* Support Ruby 3 keyword arguments. [@mame](https://github.com/mame)
* At the same time, we dropped a support that accepts options followed by mappings on `Mustermann::Mapper`. [Reference commit](https://github.com/sinatra/mustermann/pull/97/commits/4e134f5b46d8e5886b0f1590f5ff3f6ea4d2e81a)
* Improve documentation and development. [@horaciob](https://github.com/horaciob), [@epistrephein](https://github.com/epistrephein), [@jbampton](https://github.com/jbampton), [@jkowens](https://github.com/jkowens), [@junaruga](https://github.com/junaruga)
* **Mustermann 1.0.3** (2018-08-17)
* Handle `with_look_ahead` on SafeRenderer. Fixes [sinatra/sinatra#1409](https://github.com/sinatra/sinatra/issues/1409) [@namusyaka](https://github.com/namusyaka)
* Fix `EqualityMap#fetch` to be compatible with the fallback `Hash#fetch`. Fixes [#89](https://github.com/sinatra/mustermann/issues/89) [@eregon](https://github.com/eregon)
* Improve code base and documentation. [@sonots](https://github.com/sonots), [@iguchi1124](https://github.com/iguchi1124)
* **Mustermann 1.0.2** (2018-02-17)
* Look ahead same patterns as its own when concatenation. Fixes [sinatra/sinatra#1361](https://github.com/sinatra/sinatra/issues/1361) [@namusyaka](https://github.com/namusyaka)
* Improve development support and documentation. [@EdwardBetts](https://github.com/EdwardBetts), [@284km](https://github.com/284km), [@yb66](https://github.com/yb66) and [@garybernhardt](https://github.com/garybernhardt)
* **Mustermann 1.0.1** (2017-08-26)
#### Docs
* Updating readme to list Ruby 2.2 as minimum [commit](https://github.com/sinatra/mustermann/commit/7c65d9637ed81c194e3d05f0ccf3cfe76f0cf53e) (@cassidycodes)
* Fix rendering of HTML table [commit](https://github.com/sinatra/mustermann/commit/119a61f0e589cb9e917d8c901800a202bb66ff3b) (@stevenwilkin)
* Update summary and description in gemspec file. [commit](https://github.com/sinatra/mustermann/commit/04de221a809527c2be8c3f08c40a4fcd53f2bd53) (@junaruga)
#### Fixes
* avoid infinite loop by removing comments when receiving extended regexp [commit](https://github.com/sinatra/mustermann/commit/fa20301167e1b22882415f1181c5e4e2d76b6ac6) (@namusyaka)
* avoid unintended conflict of namespace [commit](https://github.com/sinatra/mustermann/commit/d3c9531d372522d693fa5f768f13dbaa1d881d88) (@namusyaka)
* use Regexp#source instead of Regexp#inspect [commit](https://github.com/sinatra/mustermann/pull/73/commits/e9213748bda1773b1ad9838ef57a296f92c471e7) (@namusyaka)
* **Mustermann 1.0.0** (2017-03-05)
* First stable release.
* Includes `mustermann`, and `mustermann-contrib` gems
* Sinatra patterns: Allow | outside of parens.
* Add concatenation support (`Mustermann::Pattern#+`).
* `Mustermann::Sinatra#|` may now generate a Sinatra pattern instead of a real composite.
* Add syntax highlighting support for composite patterns.
* Remove routers (they were out of scope for the main gem).
* Rails patterns: Add Rails 5.0 compatibility mode, make it default.
* Moved `tool` gem `EqualityMap` to `Mustermann::EqualityMap` in core
* Improve documentation.
### Development Releases
* **Mustermann 0.4.0** (2014-11-26)
* More Infos:
[RubyGems.org](https://rubygems.org/gems/mustermann/versions/0.4.0),
[RubyDoc.info](http://www.rubydoc.info/gems/mustermann/0.4.0/frames),
[GitHub.com](https://github.com/rkh/mustermann/tree/v0.4.0)
* Split into multiple gems.
* Add `Pattern#to_proc`.
* Add `Pattern#|`, `Pattern#&` and `Pattern#^`.
* Add `Pattern#peek`, `Pattern#peek_size`, `Pattern#peek_match` and `Pattern#peek_params`.
* Add `Mustermann::StringScanner`.
* Add `Pattern#to_templates`.
* Add `|` syntax to `sinatra` templates.
* Add template style placeholders to `sinatra` templates.
* Add `cake`, `express`, `flask` and `pyramid` patterns.
* Allow passing in additional value behavior directly to `Pattern#expand`.
* Fix expanding of multiple splats.
* Add expanding to `identity` patterns.
* Add `mustermann-fileutils`.
* Make expander accept hashes with string keys.
* Allow named splats to be named splat.
* Support multiple Rails versions.
* Type option can be set to nil to get the default type.
* Add `mustermann-visualizer`.
* **Mustermann 0.3.1** (2014-09-12)
* More Infos:
[RubyGems.org](https://rubygems.org/gems/mustermann/versions/0.3.1),
[RubyDoc.info](http://www.rubydoc.info/gems/mustermann/0.3.1/frames),
[GitHub.com](https://github.com/rkh/mustermann/tree/v0.3.1)
* Speed up pattern generation and matching (thanks [Daniel Mendler](https://github.com/minad))
* Small change so `Mustermann === Mustermann.new('...')` returns `true`.
* **Mustermann 0.3.0** (2014-08-18)
* More Infos:
[RubyGems.org](https://rubygems.org/gems/mustermann/versions/0.3.0),
[RubyDoc.info](http://www.rubydoc.info/gems/mustermann/0.3.0/frames),
[GitHub.com](https://github.com/rkh/mustermann/tree/v0.3.0)
* Add `regexp` pattern.
* Add named splats to Sinatra patterns.
* Add `Mustermann::Mapper`.
* Improve duck typing support.
* Improve documentation.
* **Mustermann 0.2.0** (2013-08-24)
* More Infos:
[RubyGems.org](https://rubygems.org/gems/mustermann/versions/0.2.0),
[RubyDoc.info](http://www.rubydoc.info/gems/mustermann/0.2.0/frames),
[GitHub.com](https://github.com/rkh/mustermann/tree/v0.2.0)
* Add first class expander objects.
* Add params casting for expander.
* Add simple router and rack router.
* Add weak equality map to significantly improve performance.
* Fix Ruby warnings.
* Improve documentation.
* Refactor pattern validation, AST transformations.
* Increase test coverage (from 100%+ to 100%++).
* Improve JRuby compatibility.
* Work around bug in 2.0.0-p0.
* **Mustermann 0.1.0** (2013-05-12)
* More Infos:
[RubyGems.org](https://rubygems.org/gems/mustermann/versions/0.1.0),
[RubyDoc.info](http://www.rubydoc.info/gems/mustermann/0.1.0/frames),
[GitHub.com](https://github.com/rkh/mustermann/tree/v0.1.0)
* Add `Pattern#expand` for generating strings from patterns.
* Add better internal API for working with the AST.
* Improved documentation.
* Avoids parsing the path twice when used as Sinatra extension.
* Better exceptions for unknown pattern types.
* Better handling of edge cases around extend.
* More specs to ensure API stability.
* Largely rework internals of Sinatra, Rails and Template patterns.
* **Mustermann 0.0.1** (2013-04-27)
* More Infos:
[RubyGems.org](https://rubygems.org/gems/mustermann/versions/0.0.1),
[RubyDoc.info](http://www.rubydoc.info/gems/mustermann/0.0.1/frames),
[GitHub.com](https://github.com/rkh/mustermann/tree/v0.0.1)
* Initial Release.
mustermann-1.1.1/Rakefile 0000664 0000000 0000000 00000001112 13603656127 0015354 0 ustar 00root root 0000000 0000000 task(:rspec) { ruby '-S rspec' }
task(:doc_stats) { ruby '-S yard stats' }
task default: [:rspec, :doc_stats]
task :pkg do
require 'bundler/setup'
require 'support/projects'
require 'mustermann/version'
rm_rf 'pkg'
mkdir 'pkg'
Support::Projects.each do |project|
cd project do
ruby "-S gem build #{project}.gemspec"
mv "#{project}-#{Mustermann::VERSION}.gem", '../pkg/'
end
end
end
task release: :pkg do
cd 'pkg' do
Support::Projects.each do |project|
ruby "-S gem push #{project}-#{Mustermann::VERSION}.gem"
end
end
end
mustermann-1.1.1/mustermann-contrib/ 0000775 0000000 0000000 00000000000 13603656127 0017543 5 ustar 00root root 0000000 0000000 mustermann-1.1.1/mustermann-contrib/LICENSE 0000664 0000000 0000000 00000002117 13603656127 0020551 0 ustar 00root root 0000000 0000000 Copyright (c) 2013-2017 Konstantin Haase
Copyright (c) 2016-2017 Zachary Scott
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
mustermann-1.1.1/mustermann-contrib/README.md 0000664 0000000 0000000 00000110721 13603656127 0021024 0 ustar 00root root 0000000 0000000 # The Amazing Mustermann - Contrib Edition
This is a meta gem that depends on all mustermann gems.
``` console
$ gem install mustermann-contrib
Successfully installed mustermann-1.0.0
Successfully installed mustermann-contrib-1.0.0
...
```
Also handy for your `Gemfile`:
``` ruby
gem 'mustermann-contrib'
```
Alternatively, you can use latest HEAD from github:
```ruby
github 'sinatra/mustermann' do
gem 'mustermann'
gem 'mustermann-contrib'
end
```
# CakePHP Syntax for Mustermann
This gem implements the `cake` pattern type for Mustermann. It is compatible with [CakePHP](http://cakephp.org/) 2.x and 3.x.
## Overview
**Supported options:**
`capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, and `ignore_unknown_options`.
**External documentation:**
[CakePHP 2.0 Routing](http://book.cakephp.org/2.0/en/development/routing.html),
[CakePHP 3.0 Routing](http://book.cakephp.org/3.0/en/development/routing.html)
CakePHP patterns feature captures and unnamed splats. Captures are prefixed with a colon and splats are either a single asterisk (parsing segments into an array) or a double asterisk (parsing segments as a single string).
``` ruby
require 'mustermann/cake'
Mustermann.new('/:name/*', type: :cake).params('/a/b/c') # => { name: 'a', splat: ['b', 'c'] }
Mustermann.new('/:name/**', type: :cake).params('/a/b/c') # => { name: 'a', splat: 'b/c' }
pattern = Mustermann.new('/:name')
pattern.respond_to? :expand # => true
pattern.expand(name: 'foo') # => '/foo'
pattern.respond_to? :to_templates # => true
pattern.to_templates # => ['/{name}']
```
## Syntax
Syntax Element |
Description |
:name |
Captures anything but a forward slash in a semi-greedy fashion. Capture is named name.
Capture behavior can be modified with capture and greedy option.
|
* |
Captures anything in a non-greedy fashion. Capture is named splat.
It is always an array of captures, as you can use it more than once in a pattern.
|
** |
Captures anything in a non-greedy fashion. Capture is named splat.
It is always an array of captures, as you can use it more than once in a pattern.
The value matching a single ** will be split at slashes when parsed into params.
|
/ |
Matches forward slash. Does not match URI encoded version of forward slash.
|
any other character |
Matches exactly that character or a URI encoded version of it. |
# Express Syntax for Mustermann
This gem implements the `express` pattern type for Mustermann. It is compatible with [Express](http://expressjs.com/) and [pillar.js](https://pillarjs.github.io/).
## Overview
**Supported options:**
`capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, and `ignore_unknown_options`.
**External documentation:**
[path-to-regexp](https://github.com/pillarjs/path-to-regexp#path-to-regexp),
[live demo](http://forbeslindesay.github.io/express-route-tester/)
Express patterns feature named captures (with repetition support via suffixes) that start with a colon and can have an optional regular expression constraint or unnamed captures that require a constraint.
``` ruby
require 'mustermann/express'
Mustermann.new('/:name/:rest+', type: :express).params('/a/b/c') # => { name: 'a', rest: 'b/c' }
pattern = Mustermann.new('/:name', type: :express)
pattern.respond_to? :expand # => true
pattern.expand(name: 'foo') # => '/foo'
pattern.respond_to? :to_templates # => true
pattern.to_templates # => ['/{name}']
```
## Syntax
Syntax Element |
Description |
:name |
Captures anything but a forward slash in a semi-greedy fashion. Capture is named name.
Capture behavior can be modified with capture and greedy option.
|
:name+ |
Captures one or more segments (with segments being separated by forward slashes).
Capture is named name.
Capture behavior can be modified with capture option.
|
:name* |
Captures zero or more segments (with segments being separated by forward slashes).
Capture is named name.
Capture behavior can be modified with capture option.
|
:name? |
Captures anything but a forward slash in a semi-greedy fashion. Capture is named name.
Also matches an empty string.
Capture behavior can be modified with capture and greedy option.
|
:name(regexp) |
Captures anything matching the regexp regular expression. Capture is named name.
Capture behavior can be modified with capture.
|
(regexp) |
Captures anything matching the regexp regular expression. Capture is named splat.
Capture behavior can be modified with capture.
|
/ |
Matches forward slash. Does not match URI encoded version of forward slash.
|
any other character |
Matches exactly that character or a URI encoded version of it. |
# FileUtils for Mustermann
This gem implements efficient file system operations for Mustermann patterns.
## Globbing
All operations work on a list of files described by one or more pattern.
``` ruby
require 'mustermann/file_utils'
Mustermann::FileUtils[':base.:ext'] # => ['example.txt']
Mustermann::FileUtils.glob(':base.:ext') do |file, params|
file # => "example.txt"
params # => {"base"=>"example", "ext"=>"txt"}
end
```
To avoid having to loop over all files and see if they match, it will generate a glob pattern resembling the Mustermann pattern as closely as possible.
``` ruby
require 'mustermann/file_utils'
Mustermann::FileUtils.glob_pattern('/:name') # => '/*'
Mustermann::FileUtils.glob_pattern('src/:path/:file.(js|rb)') # => 'src/**/*/*.{js,rb}'
Mustermann::FileUtils.glob_pattern('{a,b}/*', type: :shell) # => '{a,b}/*'
pattern = Mustermann.new('/foo/:page', '/bar/:page') # => #
Mustermann::FileUtils.glob_pattern(pattern) # => "{/foo/*,/bar/*}"
```
## Mapping
It is also possible to search for files and have their paths mapped onto another path in one method call:
``` ruby
require 'mustermann/file_utils'
Mustermann::FileUtils.glob_map(':base.:ext' => ':base.bak.:ext') # => {'example.txt' => 'example.bak.txt'}
Mustermann::FileUtils.glob_map(':base.:ext' => :base) { |file, mapped| mapped } # => ['example']
```
This mechanism allows things like copying, renaming and linking files:
``` ruby
require 'mustermann/file_utils'
# copies example.txt to example.bak.txt
Mustermann::FileUtils.cp(':base.:ext' => ':base.bak.:ext')
# copies Foo.app/example.txt to Foo.back.app/example.txt
Mustermann::FileUtils.cp_r(':base.:ext' => ':base.bak.:ext')
# creates a symbolic link from bin/example to lib/example.rb
Mustermann::FileUtils.ln_s('lib/:name.rb' => 'bin/:name')
```
# Flask Syntax for Mustermann
This gem implements the `flask` pattern type for Mustermann. It is compatible with [Flask](http://flask.pocoo.org/) and [Werkzeug](http://werkzeug.pocoo.org/).
## Overview
**Supported options:**
`capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, `converters` and `ignore_unknown_options`
**External documentation:**
[Werkzeug: URL Routing](http://werkzeug.pocoo.org/docs/0.9/routing/)
``` ruby
require 'mustermann/flask'
Mustermann.new('//', type: :flask).params('/a/b/c') # => { prefix: 'a', page: 'b/c' }
pattern = Mustermann.new('/', type: :flask)
pattern.respond_to? :expand # => true
pattern.expand(name: 'foo') # => '/foo'
pattern.respond_to? :to_templates # => true
pattern.to_templates # => ['/{name}']
```
## Syntax
Syntax Element |
Description |
<name> |
Captures anything but a forward slash in a semi-greedy fashion. Capture is named name.
Capture behavior can be modified with capture and greedy option.
|
<converter:name> |
Captures depending on the converter constraint. Capture is named name.
Capture behavior can be modified with capture and greedy option.
See below.
|
<converter(arguments):name> |
Captures depending on the converter constraint. Capture is named name.
Capture behavior can be modified with capture and greedy option.
Arguments are separated by comma. An argument can be a simple string, a string enclosed
in single or double quotes, or a key value pair (keys and values being separated by an
equal sign). See below.
|
/ |
Matches forward slash. Does not match URI encoded version of forward slash.
|
any other character |
Matches exactly that character or a URI encoded version of it. |
## Converters
### Builtin Converters
#### `string`
Possible arguments: `minlength`, `maxlength`, `length`
Captures anything but a forward slash in a semi-greedy fashion.
Capture behavior can be modified with capture and greedy option.
This is also the default converter.
Examples:
```
```
#### `int`
Possible arguments: `min`, `max`, `fixed_digits`
Captures digits.
Captured value will be converted to an Integer.
Examples:
```
```
#### `float`
Possible arguments: `min`, `max`
Captures digits with a dot.
Captured value will be converted to an Float.
Examples:
```
```
#### `path`
Captures anything in a non-greedy fashion.
Example:
```
```
#### `any`
Possible arguments: List of accepted strings.
Captures anything that matches one of the arguments exactly.
Example:
```
```
### Custom Converters
[Flask patterns](#-pattern-details-flask) support registering custom converters.
A converter object may implement any of the following methods:
* `convert`: Should return a block converting a string value to whatever value should end up in the `params` hash.
* `constraint`: Should return a regular expression limiting which input string will match the capture.
* `new`: Returns an object that may respond to `convert` and/or `constraint` as described above. Any arguments used for the converter inside the pattern will be passed to `new`.
``` ruby
require 'mustermann/flask'
SimpleConverter = Struct.new(:constraint, :convert)
id_converter = SimpleConverter.new(/\d/, -> s { s.to_i })
class NumConverter
def initialize(base: 10)
@base = Integer(base)
end
def convert
-> s { s.to_i(@base) }
end
def constraint
@base > 10 ? /[\da-#{(@base-1).to_s(@base)}]/ : /[0-#{@base-1}]/
end
end
pattern = Mustermann.new('///',
type: :flask, converters: { id: id_converter, num: NumConverter})
pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
```
### Global Converters
It is also possible to register a converter for all flask patterns, using `register_converter`:
``` ruby
Mustermann::Flask.register_converter(:id, id_converter)
Mustermann::Flask.register_converter(:num, NumConverter)
pattern = Mustermann.new('///', type: :flask)
pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
```
There is a handy syntax for quickly creating new converter classes: When you pass a block instead of a converter object, it will yield a generic converter with setters and getters for `convert` and `constraint`, and any arguments passed to the converter.
``` ruby
require 'mustermann/flask'
Mustermann::Flask.register_converter(:id) do |converter|
converter.constraint = /\d/
converter.convert = -> s { s.to_i }
end
Mustermann::Flask.register_converter(:num) do |converter, base: 10|
converter.constraint = base > 10 ? /[\da-#{(@base-1).to_s(base)}]/ : /[0-#{base-1}]/
converter.convert = -> s { s.to_i(base) }
end
pattern = Mustermann.new('///', type: :flask)
pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
```
### Subclassing
Registering global converters will make these available for all Flask patterns. It might even override already registered converters. This global state might break unrelated code.
It is therefore recommended that, if you don't want to pass in the converters option for every pattern, you create your own subclass of `Mustermann::Flask`.
``` ruby
require 'mustermann/flask'
MyFlask = Class.new(Mustermann::Flask)
MyFlask.register_converter(:id) do |converter|
converter.constraint = /\d/
converter.convert = -> s { s.to_i }
end
MyFlask.register_converter(:num) do |converter, base: 10|
converter.constraint = base > 10 ? /[\da-#{(@base-1).to_s(base)}]/ : /[0-#{base-1}]/
converter.convert = -> s { s.to_i(base) }
end
pattern = MyFlask.new('///')
pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
```
You can even register this type for usage with `Mustermann.new`:
``` ruby
Mustermann.register(:my_flask, MyFlask)
pattern = Mustermann.new('///', type: :my_flask)
pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
```
# Pyramid Syntax for Mustermann
This gem implements the `pyramid` pattern type for Mustermann. It is compatible with [Pyramid](http://www.pylonsproject.org/projects/pyramid/about) and [Pylons](http://www.pylonsproject.org/projects/pylons-framework/about).
## Overview
**Supported options:**
`capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode` and `ignore_unknown_options`
**External Documentation:** [Pylons Framework: URL Configuration](http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/configuration.html#url-config), [Pylons Book: Routes in Detail](http://pylonsbook.com/en/1.0/urls-routing-and-dispatch.html#routes-in-detail), [Pyramid: Route Pattern Syntax](http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/urldispatch.html#route-pattern-syntax)
``` ruby
require 'mustermann/pyramid'
Mustermann.new('/{prefix}/*suffix', type: :pyramid).params('/a/b/c') # => { prefix: 'a', suffix: ['b', 'c'] }
pattern = Mustermann.new('/{name}', type: :pyramid)
pattern.respond_to? :expand # => true
pattern.expand(name: 'foo') # => '/foo'
pattern.respond_to? :to_templates # => true
pattern.to_templates # => ['/{name}']
```
## Syntax
Syntax Element |
Description |
{name} |
Captures anything but a forward slash in a semi-greedy fashion. Capture is named name.
Capture behavior can be modified with capture and greedy option.
|
{name:regexp} |
Captures anything matching the regexp regular expression. Capture is named name.
Capture behavior can be modified with capture.
|
*name |
Captures anything in a non-greedy fashion. Capture is named name.
|
/ |
Matches forward slash. Does not match URI encoded version of forward slash.
|
any other character |
Matches exactly that character or a URI encoded version of it. |
# Rails Syntax for Mustermann
This gem implements the `rails` pattern type for Mustermann. It is compatible with [Ruby on Rails](http://rubyonrails.org/), [Journey](https://github.com/rails/journey), the [http_router gem](https://github.com/joshbuddy/http_router), [Lotus](http://lotusrb.org/) and [Scalatra](http://scalatra.org/) (if [configured](http://scalatra.org/2.3/guides/http/routes.html#toc_248))
## Overview
**Supported options:**
`capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, `version`, and `ignore_unknown_options`.
**External documentation:**
[Ruby on Rails Guides: Routing](http://guides.rubyonrails.org/routing.html).
``` ruby
require 'mustermann'
pattern = Mustermann.new('/:example', type: :rails)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => false
pattern.params("/foo.bar") # => { "example" => "foo.bar" }
pattern.params("/foo/bar") # => nil
pattern = Mustermann.new('/:example(/:optional)', type: :rails)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => true
pattern.params("/foo.bar") # => { "example" => "foo.bar", "optional" => nil }
pattern.params("/foo/bar") # => { "example" => "foo", "optional" => "bar" }
pattern = Mustermann.new('/*example', type: :rails)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => true
pattern.params("/foo.bar") # => { "example" => "foo.bar" }
pattern.params("/foo/bar") # => { "example" => "foo/bar" }
```
## Rails Compatibility
Rails syntax changed over time. You can target different Ruby on Rails versions by setting the `version` option to the desired Rails version.
The default is `4.2`. Versions prior to `2.3` are not supported.
``` ruby
require 'mustermann'
Mustermann.new('/', type: :rails, version: "2.3")
Mustermann.new('/', type: :rails, version: "3.0.0")
require 'rails'
Mustermann.new('/', type: :rails, version: Rails::VERSION::STRING)
```
## Syntax
Syntax Element |
Description |
:name |
Captures anything but a forward slash in a semi-greedy fashion. Capture is named name.
Capture behavior can be modified with tt>capture and greedy option.
|
*name |
Captures anything in a non-greedy fashion. Capture is named name.
|
(expression) |
Enclosed expression is optional. Not available in 2.3 compatibility mode. |
/ |
Matches forward slash. Does not match URI encoded version of forward slash.
|
\x |
In 3.x compatibility mode and starting with 4.2:
Matches x or URI encoded version of x. For instance \* matches *.
In 4.0 or 4.1 compatibility mode:
\ is ignored, x is parsed normally.
|
expression | expression |
3.2+ mode: This will raise a `Mustermann::ParseError`. While Ruby on Rails happily parses this character, it will result in broken routes due to a buggy implementation.
5.0 mode: It will match if any of the nested expressions matches.
|
any other character |
Matches exactly that character or a URI encoded version of it. |
# Shell Syntax for Mustermann
This gem implements the `shell` pattern type for Mustermann. It is compatible with common Unix shells (like bash or zsh).
## Overview
**Supported options:** `uri_decode` and `ignore_unknown_options`.
**External documentation:** [Ruby's fnmatch](http://www.ruby-doc.org/core-2.1.4/File.html#method-c-fnmatch), [Wikipedia: Glob (programming)](http://en.wikipedia.org/wiki/Glob_(programming))
``` ruby
require 'mustermann'
pattern = Mustermann.new('/*', type: :shell)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => false
pattern = Mustermann.new('/**/*', type: :shell)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => true
pattern = Mustermann.new('/{foo,bar}', type: :shell)
pattern === "/foo" # => true
pattern === "/bar" # => true
pattern === "/baz" # => false
```
## Syntax
Syntax Element |
Description |
* |
Matches anything but a slash. |
** |
Matches anything. |
[set] |
Matches one character in set. |
{a,b} |
Matches a or b. |
\x |
Matches x or URI encoded version of x. For instance \* matches *. |
any other character |
Matches exactly that character or a URI encoded version of it. |
# Simple Syntax for Mustermann
This gem implements the `simple` pattern type for Mustermann. It is compatible with [Sinatra](http://www.sinatrarb.com/) (1.x), [Scalatra](http://scalatra.org/) and [Dancer](http://perldancer.org/).
## Overview
**Supported options:**
`greedy`, `space_matches_plus`, `uri_decode` and `ignore_unknown_options`.
This is useful for porting an application that relies on this behavior to a later Sinatra version and to make sure Sinatra 2.0 patterns do not decrease performance. Simple patterns internally use the same code older Sinatra versions used for compiling the pattern. Error messages for broken patterns will therefore not be as informative as for other pattern implementations.
``` ruby
require 'mustermann'
pattern = Mustermann.new('/:example', type: :simple)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => false
pattern.params("/foo.bar") # => { "example" => "foo.bar" }
pattern.params("/foo/bar") # => nil
pattern = Mustermann.new('/:example/?:optional?', type: :simple)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => true
pattern.params("/foo.bar") # => { "example" => "foo.bar", "optional" => nil }
pattern.params("/foo/bar") # => { "example" => "foo", "optional" => "bar" }
pattern = Mustermann.new('/*', type: :simple)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => true
pattern.params("/foo.bar") # => { "splat" => ["foo.bar"] }
pattern.params("/foo/bar") # => { "splat" => ["foo/bar"] }
```
## Syntax
Syntax Element |
Description |
:name |
Captures anything but a forward slash in a greedy fashion. Capture is named name.
|
* |
Captures anything in a non-greedy fashion. Capture is named splat.
It is always an array of captures, as you can use * more than once in a pattern.
|
x? |
Makes x optional. For instance foo? matches foo or fo. |
/ |
Matches forward slash. Does not match URI encoded version of forward slash.
|
any special character |
Matches exactly that character or a URI encoded version of it. |
any other character |
Matches exactly that character. |
# String Scanner for Mustermann
This gem implements `Mustermann::StringScanner`, a tool inspired by Ruby's [`StringScanner`]() class.
``` ruby
require 'mustermann/string_scanner'
scanner = Mustermann::StringScanner.new("here is our example string")
scanner.scan("here") # => "here"
scanner.getch # => " "
if scanner.scan(":verb our")
scanner.scan(:noun, capture: :word)
scanner[:verb] # => "is"
scanner[:nound] # => "example"
end
scanner.rest # => "string"
```
You can pass it pattern objects directly:
``` ruby
pattern = Mustermann.new(':name')
scanner.check(pattern)
```
Or have `#scan` (and other methods) check these for you.
``` ruby
scanner.check('{name}', type: :template)
```
You can also pass in default options for ad hoc patterns when creating the scanner:
``` ruby
scanner = Mustermann::StringScanner.new(input, type: :shell)
```
# URI Template Syntax for Mustermann
This gem implements the `uri-template` (or `template`) pattern type for Mustermann. It is compatible with [RFC 6570](https://tools.ietf.org/html/rfc6570) (level 4), [JSON API](http://jsonapi.org/), [JSON Home Documents](http://tools.ietf.org/html/draft-nottingham-json-home-02) and [many more](https://code.google.com/p/uri-templates/wiki/Implementations)
## Overview
**Supported options:**
`capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, and `ignore_unknown_options`.
Please keep the following in mind:
> "Some URI Templates can be used in reverse for the purpose of variable matching: comparing the template to a fully formed URI in order to extract the variable parts from that URI and assign them to the named variables. Variable matching only works well if the template expressions are delimited by the beginning or end of the URI or by characters that cannot be part of the expansion, such as reserved characters surrounding a simple string expression. In general, regular expression languages are better suited for variable matching."
> — *RFC 6570, Sec 1.5: "Limitations"*
``` ruby
require 'mustermann'
pattern = Mustermann.new('/{example}', type: :template)
pattern === "/foo.bar" # => true
pattern === "/foo/bar" # => false
pattern.params("/foo.bar") # => { "example" => "foo.bar" }
pattern.params("/foo/bar") # => nil
pattern = Mustermann.new("{/segments*}/{page}{.ext,cmpr:2}", type: :template)
pattern.params("/a/b/c.tar.gz") # => {"segments"=>["a","b"], "page"=>"c", "ext"=>"tar", "cmpr"=>"gz"}
```
## Generating URI Templates
You do not need to use URI templates (and this gem) if all you want is reusing them for hypermedia links. Most other pattern types support generating these (via `#to_pattern`):
``` ruby
require 'mustermann'
Mustermann.new('/:name').to_templates # => ['/{name}']
```
Moreover, Mustermann's default pattern type implements a subset of URI templates (`{capture}` and `{+capture}`) and can therefore also be used for simple templates/
``` ruby
require 'mustermann'
Mustermann.new('/{name}').expand(name: "example") # => "/example"
```
## Syntax
Syntax Element |
Description |
{o var m, var m, ...} |
Captures expansion.
Operator o: + # . / ; ? & or none.
Modifier m: :num * or none.
|
/ |
Matches forward slash. Does not match URI encoded version of forward slash.
|
any other character |
Matches exactly that character or a URI encoded version of it. |
The operators `+` and `#` will always match non-greedy, whereas all other operators match semi-greedy by default.
All modifiers and operators are supported. However, it does not parse lists as single values without the *explode* modifier (aka *star*).
Parametric operators (`;`, `?` and `&`) currently only match parameters in given order.
Note that it differs from URI templates in that it takes the unescaped version of special character instead of the escaped version.
If you reuse the exact same templates and expose them via an external API meant for expansion,
you should set `uri_decode` to `false` in order to conform with the specification.
# Mustermann Pattern Visualizer
With this gem, you can visualize the internal structure of a Mustermann pattern:
* You can generate a **syntax highlighted** version of a pattern object. Both HTML/CSS based highlighting and ANSI color code based highlighting is supported.
* You can turn a pattern object into a **tree** (with ANSI color codes) representing the internal AST. This of course only works for AST based patterns.
## Syntax Highlighting

Loading `mustermann/visualizer` will automatically add `to_html` and `to_ansi` to pattern objects.
``` ruby
require 'mustermann/visualizer'
puts Mustermann.new('/:name').to_ansi
puts Mustermann.new('/:name').to_html
```
Alternatively, you can also create a separate `highlight` object, which allows finer grained control and more formats:
``` ruby
require 'mustermann/visualizer'
pattern = Mustermann.new('/:name')
highlight = Mustermann::Visualizer.highlight(pattern)
puts highlight.to_ansi
```
### `inspect` mode
By default, the highlighted string will be a colored version of `to_s`. It is also possible to produce a colored version of `inspect`
``` ruby
require 'mustermann/visualizer'
pattern = Mustermann.new('/:name')
# directly from the pattern
puts pattern.to_ansi(inspect: true)
# via the highlighter
highlight = Mustermann::Visualizer.highlight(pattern, inspect: true)
puts highlight.to_ansi
```
### Themes

element | inherits style from | default theme | note
-------------|---------------------|---------------|-------------------------
default | | #839496 | ANSI `\e[10m` if not set
special | default | #268bd2 |
capture | special | #cb4b16 |
name | | #b58900 | always inside `capture`
char | default | |
expression | capture | | only exists in URI templates
composition | special | | meta style, does not exist directly
composite | composition | | used for composite patterns (contains `root`s)
group | composition | |
union | composition | |
optional | special | |
root | default | | wraps the whole pattern
separator | char | #93a1a1 |
splat | capture | |
named_splat | splat | |
variable | capture | | always inside `expression`
escaped | char | #93a1a1 |
escaped_char | | | always inside `escaped`
quote | special | #dc322f | always outside of `root`
type | special | | always inside `composite`, outside of `root`
illegal | special | #8b0000 |
You can set theme any of the above elements. The default theme will only be applied if no custom theming is used.
``` ruby
# custom theme with highlight object
highlight = Mustermann::Visualizer.highlight(pattern, special: "#08f")
puts highlight.to_ansi
```
Themes apply both to ANSI and to HTML/CSS output. The exact ANSI code used depends on the terminal and its capabilities.
### HTML and CSS
By default, the syntax elements will be translated into `span` tags with `style` attributes.
``` ruby
Mustermann.new('/:name').to_html
```
``` html
/:name
```
You can also set the `css` option to `true` to make it include a stylesheet instead.
``` ruby
Mustermann.new('/:name').to_html(css: true)
```
``` html
/:name
```
Or you can set it to `false`, which will omit `style` attributes, but include `class` attributes.
``` html
/:name
```
It is possible to change the class prefix and the tag used.
``` ruby
Mustermann.new('/:name').to_html(css: false, class_prefix: "mm_", tag: "tt")
```
``` html
/:name
```
If you create a highlight object, you can ask it for its `stylesheet`.
``` erb
<% highlight = Mustermann::Visualizer.highlight("/:name") %>
<%= highlight.to_html(css: false) %>
```
### Other formats
If you create a highlight object, you have two other formats available: Hansi template strings and s-expression like strings. These might be useful if you want to check how a theme will be applied or as intermediate format for highlighting by other means.
``` ruby
require 'mustermann/visualizer'
highlight = Mustermann::Visualizer.highlight("/:page")
puts highlight.to_hansi_template
puts highlight.to_sexp
```
**Hansi template strings** wrap elements in tags that are similar to XML tags (though they are not, entity encoding and attributes are not supported, escaping works with a slash, so an escaped `>` would be `\>`, not `>`).
``` xml
/:page
```
The **s-expression like syntax** looks as follows:
```
(root (separator /) (capture : (name page)))
```
* An expression is enclosed by parens and contains elements separated by spaces. The first element in the expression type (corresponding to themeable elements). These are simple strings. The other elements are either expressions, simple strings or full strings.
* Simple strings do not contain spaces, parens, single or double quotes or any character that needs to be escaped.
* Full strings are Ruby strings enclosed by double quotes.
* Spaces before or after parens are optional.
### IRB/Pry integration
When `mustermann` is being loaded from within an IRB or Pry session, it will automatically load `mustermann/visualizer` too, if possible.
When displayed as result, it will be highlighted.

In Pry, this will even work when nested inside other objects (like as element on an array).
## Tree Rendering

Loading `mustermann/visualizer` will automatically add `to_tree` to pattern objects.
``` ruby
require 'mustermann/visualizer'
puts Mustermann.new("/:page(.:ext)?/*action").to_tree
```
For patterns not based on an AST (shell, simple, regexp), it will print out a single line:
pattern (not AST based) "/example"
It will display a tree for identity patterns. While these are not based on an AST internally, Mustermann supports generating an AST for these patterns.
mustermann-1.1.1/mustermann-contrib/examples/ 0000775 0000000 0000000 00000000000 13603656127 0021361 5 ustar 00root root 0000000 0000000 mustermann-1.1.1/mustermann-contrib/examples/highlighting.rb 0000664 0000000 0000000 00000002274 13603656127 0024360 0 ustar 00root root 0000000 0000000 require 'bundler/setup'
require 'mustermann/visualizer'
Hansi.mode = ARGV[0].to_i if ARGV.any?
def self.example(type, *patterns)
print Hansi.render(:bold, " #{type}: ".ljust(14))
patterns.each do |pattern|
pattern = Mustermann.new(pattern, type: type)
space_after = pattern.to_s.size > 24 ? " " : " " * (25 - pattern.to_s.size)
highlight = Mustermann::Visualizer.highlight(pattern, inspect: true)
print highlight.to_ansi + space_after
end
puts
end
puts
example(:cake, '/:prefix/**')
example(:express, '/:prefix+/:id(\d+)', '/:page/:slug+')
example(:flask, '//', '/user/')
example(:identity, '/image.png')
example(:pyramid, '/{prefix:.*}/{id}', '/{page}/*slug')
example(:rails, '/:slug(.:ext)')
example(:regexp, '/(?[^/]+)', '/(?:page|user)/(\d+)')
example(:shell, '/**/*', '/\{a,b\}/{a,b}')
example(:simple, '/:page/*slug')
example(:sinatra, '/:page/*slug', '/users/{id}?')
example(:template, '/{+pre}/{page}{?q,p}', '/users/{id}?')
puts
example(:composition)
composite = Mustermann.new("/{a}", "/{b}/{c}")
puts " " + composite.to_ansi
puts " " + (Mustermann.new("/") ^ composite).to_ansi
puts mustermann-1.1.1/mustermann-contrib/highlighting.png 0000664 0000000 0000000 00000253446 13603656127 0022734 0 ustar 00root root 0000000 0000000 PNG
IHDR a ?
iCCPICC Profile H
wTS-@轷 { "ͮȠ#(:TGG@ƂbȠ>6T
z'[gg}V> Z O(LG 22E!n!PA A
txB & ucqMtB
> |(-e|cYb3sWV q@ tO$ r1`uQe&db,O%`|c|߈s22c:Mcj
c/[}O3sg_3Cِ&9cJ
$bse
a%v"yjWʆ