mustermann-fileutils 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f3a4166e01feb70f7f2ff3638a75485a5b7dac48
4
+ data.tar.gz: 3e30d5bce9ecaa69a9c06710553ac9083a1600b9
5
+ SHA512:
6
+ metadata.gz: 2dce4ceac77fff3bb5d4f6432c13051d7e921535dee68fcb927b9b547b782e8e249b9cc2478a90fbf3bfd5aa5ce12cd4f9113ee3f0c4169889ec590f5ff7d4cc
7
+ data.tar.gz: fffe449bde8a545785cd3102520fb892241e5e130728e322107d9482e504c7cd94e8dda958cd200307366a26678c4aa44d24643d52cfc103c5c4318e9af28989
@@ -0,0 +1,57 @@
1
+ # FileUtils for Mustermann
2
+
3
+ This gem implements efficient file system operations for Mustermann patterns.
4
+
5
+ ## Globbing
6
+
7
+ All operations work on a list of files described by one or more pattern.
8
+
9
+ ``` ruby
10
+ require 'mustermann/file_utils'
11
+
12
+ Mustermann::FileUtils[':base.:ext'] # => ['example.txt']
13
+
14
+ Mustermann::FileUtils.glob(':base.:ext') do |file, params|
15
+ file # => "example.txt"
16
+ params # => {"base"=>"example", "ext"=>"txt"}
17
+ end
18
+ ```
19
+
20
+ 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.
21
+
22
+ ``` ruby
23
+ require 'mustermann/file_utils'
24
+
25
+ Mustermann::FileUtils.glob_pattern('/:name') # => '/*'
26
+ Mustermann::FileUtils.glob_pattern('src/:path/:file.(js|rb)') # => 'src/**/*/*.{js,rb}'
27
+ Mustermann::FileUtils.glob_pattern('{a,b}/*', type: :shell) # => '{a,b}/*'
28
+
29
+ pattern = Mustermann.new('/foo/:page', '/bar/:page') # => #<Mustermann::Composite:...>
30
+ Mustermann::FileUtils.glob_pattern(pattern) # => "{/foo/*,/bar/*}"
31
+ ```
32
+
33
+ ## Mapping
34
+
35
+ It is also possible to search for files and have their paths mapped onto another path in one method call:
36
+
37
+ ``` ruby
38
+ require 'mustermann/file_utils'
39
+
40
+ Mustermann::FileUtils.glob_map(':base.:ext' => ':base.bak.:ext') # => {'example.txt' => 'example.bak.txt'}
41
+ Mustermann::FileUtils.glob_map(':base.:ext' => :base) { |file, mapped| mapped } # => ['example']
42
+ ```
43
+
44
+ This mechanism allows things like copying, renaming and linking files:
45
+
46
+ ``` ruby
47
+ require 'mustermann/file_utils'
48
+
49
+ # copies example.txt to example.bak.txt
50
+ Mustermann::FileUtils.cp(':base.:ext' => ':base.bak.:ext')
51
+
52
+ # copies Foo.app/example.txt to Foo.back.app/example.txt
53
+ Mustermann::FileUtils.cp_r(':base.:ext' => ':base.bak.:ext')
54
+
55
+ # creates a symbolic link from bin/example to lib/example.rb
56
+ Mustermann::FileUtils.ln_s('lib/:name.rb' => 'bin/:name')
57
+ ```
@@ -0,0 +1,217 @@
1
+ require 'mustermann'
2
+ require 'mustermann/file_utils/glob_pattern'
3
+ require 'mustermann/mapper'
4
+ require 'fileutils'
5
+
6
+ module Mustermann
7
+ # Implements handy file operations using patterns.
8
+ module FileUtils
9
+ extend self
10
+
11
+ # Turn a Mustermann pattern into glob pattern.
12
+ #
13
+ # @example
14
+ # require 'mustermann/file_utils'
15
+ #
16
+ # Mustermann::FileUtils.glob_pattern('/:name') # => '/*'
17
+ # Mustermann::FileUtils.glob_pattern('src/:path/:file.(js|rb)') # => 'src/**/*/*.{js,rb}'
18
+ # Mustermann::FileUtils.glob_pattern('{a,b}/*', type: :shell) # => '{a,b}/*'
19
+ #
20
+ # pattern = Mustermann.new('/foo/:page', '/bar/:page') # => #<Mustermann::Composite:...>
21
+ # Mustermann::FileUtils.glob_pattern(pattern) # => "{/foo/*,/bar/*}"
22
+ #
23
+ # @param [Object] pattern the object to turn into a glob pattern.
24
+ # @return [String] the glob pattern
25
+ def glob_pattern(*pattern, **options)
26
+ pattern_with_glob_pattern(*pattern, **options).last
27
+ end
28
+
29
+ # Uses the given pattern(s) to search for files and directories.
30
+ #
31
+ # @example
32
+ # require 'mustermann/file_utils'
33
+ # Mustermann::FileUtils.glob(':base.:ext') # => ['example.txt']
34
+ #
35
+ # Mustermann::FileUtils.glob(':base.:ext') do |file, params|
36
+ # file # => "example.txt"
37
+ # params # => {"base"=>"example", "ext"=>"txt"}
38
+ # end
39
+ def glob(*pattern, **options, &block)
40
+ raise ArgumentError, "no pattern given" if pattern.empty?
41
+ pattern, glob_pattern = pattern_with_glob_pattern(*pattern, **options)
42
+ results = [] unless block
43
+ Dir.glob(glob_pattern) do |result|
44
+ next unless params = pattern.params(result)
45
+ block ? block[result, params] : results << result
46
+ end
47
+ results
48
+ end
49
+
50
+ # Allows to search for files an map these onto other strings.
51
+ #
52
+ # @example
53
+ # require 'mustermann/file_utils'
54
+ #
55
+ # Mustermann::FileUtils.glob_map(':base.:ext' => ':base.bak.:ext') # => {'example.txt' => 'example.bak.txt'}
56
+ # Mustermann::FileUtils.glob_map(':base.:ext' => :base) { |file, mapped| mapped } # => ['example']
57
+ #
58
+ # @see Mustermann::Mapper
59
+ def glob_map(map = {}, **options, &block)
60
+ map = Mapper === map ? map : Mapper.new(map, **options)
61
+ mapped = glob(*map.to_h.keys).map { |f| [f, unescape(map[f])] }
62
+ block ? mapped.map(&block) : Hash[mapped]
63
+ end
64
+
65
+ # Copies files based on a pattern mapping.
66
+ #
67
+ # @example
68
+ # require 'mustermann/file_utils'
69
+ #
70
+ # # copies example.txt to example.bak.txt
71
+ # Mustermann::FileUtils.cp(':base.:ext' => ':base.bak.:ext')
72
+ #
73
+ # @see #glob_map
74
+ def cp(map = {}, recursive: false, **options)
75
+ utils_opts, opts = split_options(:preserve, :dereference_root, :remove_destination, **options)
76
+ cp_method = recursive ? :cp_r : :cp
77
+ glob_map(map, **opts) { |o,n| f.send(cp_method, o, n, **utils_opts) }
78
+ end
79
+
80
+
81
+ # Copies files based on a pattern mapping, recursively.
82
+ #
83
+ # @example
84
+ # require 'mustermann/file_utils'
85
+ #
86
+ # # copies Foo.app/example.txt to Foo.back.app/example.txt
87
+ # Mustermann::FileUtils.cp_r(':base.:ext' => ':base.bak.:ext')
88
+ #
89
+ # @see #glob_map
90
+ def cp_r(map = {}, **options)
91
+ cp(map, recursive: true, **options)
92
+ end
93
+
94
+ # Moves files based on a pattern mapping.
95
+ #
96
+ # @example
97
+ # require 'mustermann/file_utils'
98
+ #
99
+ # # moves example.txt to example.bak.txt
100
+ # Mustermann::FileUtils.mv(':base.:ext' => ':base.bak.:ext')
101
+ #
102
+ # @see #glob_map
103
+ def mv(map = {}, **options)
104
+ utils_opts, opts = split_options(**options)
105
+ glob_map(map, **opts) { |o,n| f.mv(o, n, **utils_opts) }
106
+ end
107
+
108
+
109
+ # Creates links based on a pattern mapping.
110
+ #
111
+ # @example
112
+ # require 'mustermann/file_utils'
113
+ #
114
+ # # creates a link from bin/example to lib/example.rb
115
+ # Mustermann::FileUtils.ln('lib/:name.rb' => 'bin/:name')
116
+ #
117
+ # @see #glob_map
118
+ def ln(map = {}, symbolic: false, **options)
119
+ utils_opts, opts = split_options(**options)
120
+ link_method = symbolic ? :ln_s : :ln
121
+ glob_map(map, **opts) { |o,n| f.send(link_method, o, n, **utils_opts) }
122
+ end
123
+
124
+ # Creates symbolic links based on a pattern mapping.
125
+ #
126
+ # @example
127
+ # require 'mustermann/file_utils'
128
+ #
129
+ # # creates a symbolic link from bin/example to lib/example.rb
130
+ # Mustermann::FileUtils.ln_s('lib/:name.rb' => 'bin/:name')
131
+ #
132
+ # @see #glob_map
133
+ def ln_s(map = {}, **options)
134
+ ln(map, symbolic: true, **options)
135
+ end
136
+
137
+ # Creates symbolic links based on a pattern mapping.
138
+ # Overrides potentailly existing files.
139
+ #
140
+ # @example
141
+ # require 'mustermann/file_utils'
142
+ #
143
+ # # creates a symbolic link from bin/example to lib/example.rb
144
+ # Mustermann::FileUtils.ln_sf('lib/:name.rb' => 'bin/:name')
145
+ #
146
+ # @see #glob_map
147
+ def ln_sf(map = {}, **options)
148
+ ln(map, symbolic: true, force: true, **options)
149
+ end
150
+
151
+
152
+ # Splits options into those meant for Mustermann and those
153
+ # meant for ::FileUtils.
154
+ #
155
+ # @!visibility private
156
+ def split_options(*utils_option_names, **options)
157
+ utils_options, pattern_options = {}, {}
158
+ utils_option_names += %i[force noop verbose]
159
+
160
+ options.each do |key, value|
161
+ list = utils_option_names.include?(key) ? utils_options : pattern_options
162
+ list[key] = value
163
+ end
164
+
165
+ [utils_options, pattern_options]
166
+ end
167
+
168
+ # Create a Mustermann pattern from whatever the input is and turn it into
169
+ # a glob pattern.
170
+ #
171
+ # @!visibility private
172
+ def pattern_with_glob_pattern(*pattern, **options)
173
+ options[:uri_decode] ||= false
174
+ pattern = Mustermann.new(*pattern.flatten, **options)
175
+ @glob_patterns ||= {}
176
+ @glob_patterns[pattern] ||= GlobPattern.generate(pattern)
177
+ [pattern, @glob_patterns[pattern]]
178
+ end
179
+
180
+ # The FileUtils method to use.
181
+ # @!visibility private
182
+ def f
183
+ ::FileUtils
184
+ end
185
+
186
+ # Unescape an URI escaped string.
187
+ # @!visibility private
188
+ def unescape(string)
189
+ @uri ||= URI::Parser.new
190
+ @uri.unescape(string)
191
+ end
192
+
193
+ # Create a new version of Mustermann::FileUtils using a different ::FileUtils module.
194
+ # @see DryRun
195
+ # @!visibility private
196
+ def with_file_utils(&block)
197
+ Module.new do
198
+ include Mustermann::FileUtils
199
+ define_method(:f, &block)
200
+ private(:f)
201
+ extend self
202
+ end
203
+ end
204
+
205
+ private :pattern_with_glob_pattern, :split_options, :f, :unescape
206
+
207
+ alias_method :copy, :cp
208
+ alias_method :move, :mv
209
+ alias_method :link, :ln
210
+ alias_method :symlink, :ln_s
211
+ alias_method :[], :glob
212
+
213
+ DryRun ||= with_file_utils { ::FileUtils::DryRun }
214
+ NoWrite ||= with_file_utils { ::FileUtils::NoWrite }
215
+ Verbose ||= with_file_utils { ::FileUtils::Verbose }
216
+ end
217
+ end
@@ -0,0 +1,39 @@
1
+ require 'mustermann/ast/translator'
2
+
3
+ module Mustermann
4
+ module FileUtils
5
+ # AST Translator to turn Mustermann patterns into glob patterns.
6
+ # @!visibility private
7
+ class GlobPattern < Mustermann::AST::Translator
8
+ # Character that need to be escaped in glob patterns.
9
+ # @!visibility private
10
+ ESCAPE = %w([ ] { } * ** \\)
11
+
12
+ # Turn a Mustermann pattern into glob pattern.
13
+ # @param [#to_glob, #to_ast, Object] pattern the object to turn into a glob pattern.
14
+ # @return [String] the glob pattern
15
+ # @!visibility private
16
+ def self.generate(pattern)
17
+ return pattern.to_glob if pattern.respond_to? :to_glob
18
+ return new.translate(pattern.to_ast) if pattern.respond_to? :to_ast
19
+ return "**/*" unless pattern.is_a? Mustermann::Composite
20
+ "{#{pattern.patterns.map { |p| generate(p) }.join(',')}}"
21
+ end
22
+
23
+ translate(:root, :group, :expression) { t(payload) || "" }
24
+ translate(:separator, :char) { t.escape(payload) }
25
+ translate(:capture) { constraint ? "**/*" : "*" }
26
+ translate(:optional) { "{#{t(payload)},}" }
27
+ translate(:named_splat, :splat) { "**/*" }
28
+ translate(:with_look_ahead) { t(head) + t(payload) }
29
+ translate(:union) { "{#{payload.map { |e| t(e) }.join(',')}}" }
30
+ translate(Array) { map { |e| t(e) }.join }
31
+
32
+ # Escape with a slash rather than URI escaping.
33
+ # @!visibility private
34
+ def escape(char)
35
+ ESCAPE.include?(char) ? "\\#{char}" : char
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1 @@
1
+ require 'mustermann/file_utils'
@@ -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-fileutils"
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{File Utils for Mustermann}
11
+ s.description = %q{Operate efficiently on your file system using Mustermann}
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,119 @@
1
+ require 'support'
2
+ require 'mustermann/file_utils'
3
+
4
+ describe Mustermann::FileUtils do
5
+ subject(:utils) { Mustermann::FileUtils }
6
+ include FileUtils
7
+
8
+ before do
9
+ @pwd = Dir.pwd
10
+ @tmp_dir = File.join(__dir__, 'tmp')
11
+
12
+ mkdir_p(@tmp_dir)
13
+ chdir(@tmp_dir)
14
+
15
+ touch("foo.txt")
16
+ touch("foo.rb")
17
+ touch("bar.txt")
18
+ touch("bar.rb")
19
+ end
20
+
21
+ after do
22
+ chdir(@pwd) if @pwd
23
+ rm_rf(@tmp_dir) if @tmp_dir
24
+ end
25
+
26
+ describe :glob_pattern do
27
+ example { utils.glob_pattern('/:foo') .should be == '/*' }
28
+ example { utils.glob_pattern('/*foo') .should be == '/**/*' }
29
+ example { utils.glob_pattern('/(ab|c)?/:foo/d/*bar') .should be == '/{{ab,c},}/*/d/**/*' }
30
+ example { utils.glob_pattern('/a', '/b') .should be == '{/a,/b}' }
31
+ example { utils.glob_pattern('**/*', type: :shell) .should be == '**/*' }
32
+ example { utils.glob_pattern(/can't parse this/) .should be == '**/*' }
33
+ example { utils.glob_pattern('/foo', type: :identity) .should be == '/foo' }
34
+ example { utils.glob_pattern('/fo*', type: :identity) .should be == '/fo\\*' }
35
+ end
36
+
37
+ describe :glob do
38
+ example do
39
+ utils.glob(":name.txt").sort.should be == ['bar.txt', 'foo.txt']
40
+ end
41
+
42
+ example do
43
+ extensions = []
44
+ utils.glob("foo.:ext") { |file, params| extensions << params['ext'] }
45
+ extensions.sort.should be == ['rb', 'txt']
46
+ end
47
+
48
+ example do
49
+ utils.glob(":name.:ext", capture: { ext: 'rb', name: 'foo' }).should be == ['foo.rb']
50
+ end
51
+ end
52
+
53
+ describe :glob_map do
54
+ example do
55
+ utils.glob_map(':name.rb' => ':name/init.rb').should be == {
56
+ "bar.rb" => "bar/init.rb",
57
+ "foo.rb" => "foo/init.rb"
58
+ }
59
+ end
60
+
61
+ example do
62
+ result = {}
63
+ returned = utils.glob_map(':name.rb' => ':name/init.rb') { |k, v| result[v] = k.upcase }
64
+ returned.sort .should be == ["BAR.RB", "FOO.RB"]
65
+ result["bar/init.rb"] .should be == "BAR.RB"
66
+ end
67
+ end
68
+
69
+ describe :cp do
70
+ example do
71
+ utils.cp(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
72
+ File.should be_exist('foo.ruby')
73
+ File.should be_exist('bar.md')
74
+ File.should be_exist('bar.txt')
75
+ end
76
+ end
77
+
78
+ describe :cp_r do
79
+ example do
80
+ mkdir_p "foo/bar"
81
+ utils.cp_r('foo/:name' => :name)
82
+ File.should be_directory('bar')
83
+ end
84
+ end
85
+
86
+ describe :mv do
87
+ example do
88
+ utils.mv(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
89
+ File.should be_exist('foo.ruby')
90
+ File.should be_exist('bar.md')
91
+ File.should_not be_exist('bar.txt')
92
+ end
93
+ end
94
+
95
+ describe :ln do
96
+ example do
97
+ utils.ln(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
98
+ File.should be_exist('foo.ruby')
99
+ File.should be_exist('bar.md')
100
+ File.should be_exist('bar.txt')
101
+ end
102
+ end
103
+
104
+ describe :ln_s do
105
+ example do
106
+ utils.ln_s(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
107
+ File.should be_symlink('foo.ruby')
108
+ File.should be_symlink('bar.md')
109
+ File.should be_exist('bar.txt')
110
+ end
111
+ end
112
+
113
+ describe :ln_sf do
114
+ example do
115
+ utils.ln_sf(':name.rb' => ':name.txt')
116
+ File.should be_symlink('foo.txt')
117
+ end
118
+ end
119
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mustermann-fileutils
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: Operate efficiently on your file system using Mustermann
28
+ email: konstantin.mailinglists@googlemail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - README.md
34
+ - lib/mustermann/file_utils.rb
35
+ - lib/mustermann/file_utils/glob_pattern.rb
36
+ - lib/mustermann/fileutils.rb
37
+ - mustermann-fileutils.gemspec
38
+ - spec/file_utils_spec.rb
39
+ homepage: https://github.com/rkh/mustermann
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 2.1.0
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 2.4.3
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: File Utils for Mustermann
63
+ test_files:
64
+ - spec/file_utils_spec.rb
65
+ has_rdoc: