mustermann-shell 0.4.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 70b7a9e2e10df85b4f20be29bf3ef41935a2a73b
4
+ data.tar.gz: 047921a376ba9ae7317abee42b1044203b3acc6e
5
+ SHA512:
6
+ metadata.gz: 5f62850ed4adfa6e90b205c249a7790e2ef0fa4bdf43fe7e0bca7c8d88e4a1f8ad3180fd350866ddcbc2c951ed1e5149d098d1e01435867b5ba7fbadc412ddf3
7
+ data.tar.gz: 1ec90f73ab2938ea02a07b7eb5dc047e20b474e1392a3aa1f1228dc2a42b8d659a7409fcee116a46624291a87791f282d07b60e62ee04803c5f4c31181ba46dc
@@ -0,0 +1,63 @@
1
+ # Shell Syntax for Mustermann
2
+
3
+ This gem implements the `rails` pattern type for Mustermann. It is compatible with common Unix shells (like bash or zsh).
4
+
5
+ ## Overview
6
+
7
+ **Supported options:** `uri_decode` and `ignore_unknown_options`.
8
+
9
+ **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))
10
+
11
+ ``` ruby
12
+ require 'mustermann'
13
+
14
+ pattern = Mustermann.new('/*', type: :shell)
15
+ pattern === "/foo.bar" # => true
16
+ pattern === "/foo/bar" # => false
17
+
18
+ pattern = Mustermann.new('/**/*', type: :shell)
19
+ pattern === "/foo.bar" # => true
20
+ pattern === "/foo/bar" # => true
21
+
22
+ pattern = Mustermann.new('/{foo,bar}', type: :shell)
23
+ pattern === "/foo" # => true
24
+ pattern === "/bar" # => true
25
+ pattern === "/baz" # => false
26
+ ```
27
+
28
+ ## Syntax
29
+
30
+ <table>
31
+ <thead>
32
+ <tr>
33
+ <th>Syntax Element</th>
34
+ <th>Description</th>
35
+ </tr>
36
+ </thead>
37
+ <tbody>
38
+ <tr>
39
+ <td><b>*</b></td>
40
+ <td>Matches anything but a slash.</td>
41
+ </tr>
42
+ <tr>
43
+ <td><b>**</b></td>
44
+ <td>Matches anything.</td>
45
+ </tr>
46
+ <tr>
47
+ <td><b>[</b><i>set</i><b>]</b></td>
48
+ <td>Matches one character in <i>set</i>.</td>
49
+ </tr>
50
+ <tr>
51
+ <td><b>&#123;</b><i>a</i>,<i>b</i><b>&#125;</b></td>
52
+ <td>Matches <i>a</i> or <i>b</i>.</td>
53
+ </tr>
54
+ <tr>
55
+ <td><b>\</b><i>x</i></td>
56
+ <td>Matches <i>x</i> or URI encoded version of <i>x</i>. For instance <tt>\*</tt> matches <tt>*</tt>.</td>
57
+ </tr>
58
+ <tr>
59
+ <td><i>any other character</i></td>
60
+ <td>Matches exactly that character or a URI encoded version of it.</td>
61
+ </tr>
62
+ </tbody>
63
+ </table>
@@ -0,0 +1,55 @@
1
+ require 'mustermann'
2
+ require 'mustermann/pattern'
3
+ require 'mustermann/simple_match'
4
+
5
+ module Mustermann
6
+ # Matches strings that are identical to the pattern.
7
+ #
8
+ # @example
9
+ # Mustermann.new('/*.*', type: :shell) === '/bar' # => false
10
+ #
11
+ # @see Mustermann::Pattern
12
+ # @see file:README.md#shell Syntax description in the README
13
+ class Shell < Pattern
14
+ register :shell
15
+
16
+ # @!visibility private
17
+ # @return [#highlight, nil]
18
+ # highlighing logic for mustermann-visualizer,
19
+ # nil if mustermann-visualizer hasn't been loaded
20
+ def highlighter
21
+ return unless defined? Mustermann::Visualizer::Highlighter
22
+ @@highlighter ||= Mustermann::Visualizer::Highlighter.create do
23
+ on('\\') { |matched| escaped(matched, scanner.getch) }
24
+ on(/[\*\[\]]/, :special)
25
+ on("{") { nested(:union, ?{, ?}, ?,) }
26
+ end
27
+ end
28
+
29
+ # @param (see Mustermann::Pattern#initialize)
30
+ # @return (see Mustermann::Pattern#initialize)
31
+ # @see (see Mustermann::Pattern#initialize)
32
+ def initialize(string, **options)
33
+ @flags = File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB
34
+ super(string, **options)
35
+ end
36
+
37
+ # @param (see Mustermann::Pattern#===)
38
+ # @return (see Mustermann::Pattern#===)
39
+ # @see (see Mustermann::Pattern#===)
40
+ def ===(string)
41
+ File.fnmatch? @string, unescape(string), @flags
42
+ end
43
+
44
+ # @param (see Mustermann::Pattern#peek_size)
45
+ # @return (see Mustermann::Pattern#peek_size)
46
+ # @see (see Mustermann::Pattern#peek_size)
47
+ def peek_size(string)
48
+ @peek_string ||= @string + "{**,/**,/**/*}"
49
+ super if File.fnmatch? @peek_string, unescape(string), @flags
50
+ end
51
+
52
+ # Used by {Mustermann::FileUtils} to not use a generic glob pattern.
53
+ alias_method :to_glob, :to_s
54
+ end
55
+ end
@@ -0,0 +1,18 @@
1
+ $:.unshift File.expand_path("../../mustermann/lib", __FILE__)
2
+ require "mustermann/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "mustermann-shell"
6
+ s.version = Mustermann::VERSION
7
+ s.author = "Konstantin Haase"
8
+ s.email = "konstantin.mailinglists@googlemail.com"
9
+ s.homepage = "https://github.com/rkh/mustermann"
10
+ s.summary = %q{Shell syntax for Mustermann}
11
+ s.description = %q{Adds Shell style patterns to Mustermman}
12
+ s.license = 'MIT'
13
+ s.required_ruby_version = '>= 2.1.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
+ end
@@ -0,0 +1,147 @@
1
+ require 'support'
2
+ require 'mustermann/shell'
3
+
4
+ describe Mustermann::Shell do
5
+ extend Support::Pattern
6
+
7
+ pattern '' do
8
+ it { should match('') }
9
+ it { should_not match('/') }
10
+
11
+ it { should_not respond_to(:expand) }
12
+ it { should_not respond_to(:to_templates) }
13
+ end
14
+
15
+ pattern '/' do
16
+ it { should match('/') }
17
+ it { should_not match('/foo') }
18
+
19
+ example { pattern.params('/').should be == {} }
20
+ example { pattern.params('').should be_nil }
21
+ end
22
+
23
+ pattern '/foo' do
24
+ it { should match('/foo') }
25
+ it { should_not match('/bar') }
26
+ it { should_not match('/foo.bar') }
27
+ end
28
+
29
+ pattern '/foo/bar' do
30
+ it { should match('/foo/bar') }
31
+ it { should match('/foo%2Fbar') }
32
+ it { should match('/foo%2fbar') }
33
+ end
34
+
35
+ pattern '/*/bar' do
36
+ it { should match('/foo/bar') }
37
+ it { should match('/bar/bar') }
38
+ it { should match('/foo%2Fbar') }
39
+ it { should match('/foo%2fbar') }
40
+ it { should_not match('/foo/foo/bar') }
41
+ it { should_not match('/bar/foo') }
42
+ end
43
+
44
+ pattern '/**/foo' do
45
+ it { should match('/a/b/c/foo') }
46
+ it { should match('/a/b/c/foo') }
47
+ it { should match('/a/.b/c/foo') }
48
+ it { should match('/a/.b/c/foo') }
49
+ end
50
+
51
+ pattern '/:foo' do
52
+ it { should match('/:foo') }
53
+ it { should match('/%3Afoo') }
54
+ it { should_not match('/foo') }
55
+ it { should_not match('/foo?') }
56
+ it { should_not match('/foo/bar') }
57
+ it { should_not match('/') }
58
+ it { should_not match('/foo/') }
59
+ end
60
+
61
+ pattern '/föö' do
62
+ it { should match("/f%C3%B6%C3%B6") }
63
+ end
64
+
65
+ pattern '/test$/' do
66
+ it { should match('/test$/') }
67
+ end
68
+
69
+ pattern '/te+st/' do
70
+ it { should match('/te+st/') }
71
+ it { should_not match('/test/') }
72
+ it { should_not match('/teest/') }
73
+ end
74
+
75
+ pattern "/path with spaces" do
76
+ it { should match('/path%20with%20spaces') }
77
+ it { should_not match('/path%2Bwith%2Bspaces') }
78
+ it { should_not match('/path+with+spaces') }
79
+ end
80
+
81
+ pattern '/foo&bar' do
82
+ it { should match('/foo&bar') }
83
+ end
84
+
85
+ pattern '/test.bar' do
86
+ it { should match('/test.bar') }
87
+ it { should_not match('/test0bar') }
88
+ end
89
+
90
+ pattern '/{foo,bar}' do
91
+ it { should match('/foo') }
92
+ it { should match('/bar') }
93
+ it { should_not match('/foobar') }
94
+ end
95
+
96
+ pattern '/foo/bar', uri_decode: false do
97
+ it { should match('/foo/bar') }
98
+ it { should_not match('/foo%2Fbar') }
99
+ it { should_not match('/foo%2fbar') }
100
+ end
101
+
102
+ pattern "/path with spaces", uri_decode: false do
103
+ it { should_not match('/path%20with%20spaces') }
104
+ it { should_not match('/path%2Bwith%2Bspaces') }
105
+ it { should_not match('/path+with+spaces') }
106
+ end
107
+
108
+ describe :=~ do
109
+ example { '/foo'.should be =~ Mustermann::Shell.new('/foo') }
110
+ end
111
+
112
+ context "peeking" do
113
+ subject(:pattern) { Mustermann::Shell.new("foo*/") }
114
+
115
+ describe :peek_size do
116
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar/".size }
117
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar/".size }
118
+ example { pattern.peek_size("/foo bar") .should be_nil }
119
+
120
+ context 'with just * as pattern' do
121
+ subject(:pattern) { Mustermann::Shell.new('*') }
122
+ example { pattern.peek_size('foo') .should be == 3 }
123
+ example { pattern.peek_size('foo/bar') .should be == 3 }
124
+ example { pattern.peek_size('foo/bar/baz') .should be == 3 }
125
+ example { pattern.peek_size('foo/bar/baz/blah') .should be == 3 }
126
+ end
127
+ end
128
+
129
+ describe :peek_match do
130
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar/" }
131
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar/" }
132
+ example { pattern.peek_match("/foo bar") .should be_nil }
133
+ end
134
+
135
+ describe :peek_params do
136
+ example { pattern.peek_params("foo bar/blah") .should be == [{}, "foo bar/".size] }
137
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{}, "foo%20bar/".size] }
138
+ example { pattern.peek_params("/foo bar") .should be_nil }
139
+ end
140
+ end
141
+
142
+ context "highlighting" do
143
+ let(:pattern) { Mustermann::Shell.new("/**,*/\\*/{a,b}") }
144
+ subject(:sexp) { Mustermann::Visualizer.highlight(pattern).to_sexp }
145
+ it { should be == '(root (separator /) (special *) (special *) (char ,) (special *) (separator /) (escaped "\\\\" (escaped_char *)) (separator /) (union { (root (char a)) ,(root (char b)) }))' }
146
+ end
147
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mustermann-shell
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Konstantin Haase
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mustermann
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.4.0
27
+ description: Adds Shell style patterns to Mustermman
28
+ email: konstantin.mailinglists@googlemail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - README.md
34
+ - lib/mustermann/shell.rb
35
+ - mustermann-shell.gemspec
36
+ - spec/shell_spec.rb
37
+ homepage: https://github.com/rkh/mustermann
38
+ licenses:
39
+ - MIT
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 2.1.0
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 2.4.3
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: Shell syntax for Mustermann
61
+ test_files:
62
+ - spec/shell_spec.rb
63
+ has_rdoc: