fled 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +230 -0
- data/bin/fled +152 -0
- data/lib/dtc/utils/dsldsl.rb +259 -0
- data/lib/dtc/utils/exec.rb +134 -0
- data/lib/dtc/utils/file_visitor.rb +78 -0
- data/lib/dtc/utils/interactive_edit.rb +81 -0
- data/lib/dtc/utils/mini_select.rb +177 -0
- data/lib/dtc/utils.rb +9 -0
- data/lib/fled/file_listing.rb +260 -0
- data/lib/fled.rb +38 -0
- data/tests/helper.rb +90 -0
- data/tests/readme.rb +256 -0
- data/tests/test_operations.rb +229 -0
- metadata +59 -0
data/README.md
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
|
2
|
+
# FlEd
|
3
|
+
|
4
|
+
`fled` lets you organise your files and folders in your favourite editor
|
5
|
+
|
6
|
+
## Disclaimer
|
7
|
+
|
8
|
+
Warning: This is a very dangerous tool. The author recommends you do not
|
9
|
+
use it. The author cannot be held responsible in any case.
|
10
|
+
|
11
|
+
## Introduction
|
12
|
+
|
13
|
+
`fled` enumerates a folder and its files, and generates a text listing.
|
14
|
+
You can then edit that listing in your favourite editor, and save changes.
|
15
|
+
`fled` then reloads those changes, and prints a shell script that would move
|
16
|
+
your files and folders around as-per your edits.
|
17
|
+
|
18
|
+
**You should review that shell script very carefully before running it.**
|
19
|
+
|
20
|
+
### Philosophy
|
21
|
+
|
22
|
+
`fled` only generates text, it does not perform any operation directly.
|
23
|
+
|
24
|
+
The design optimises for making the edits very simple. The consequence of
|
25
|
+
this is that very small edits can have large consequences, which makes
|
26
|
+
this a **very dangerous** tool. But so is `rm` and the rest of the shell anyway...
|
27
|
+
|
28
|
+
### Caveats
|
29
|
+
|
30
|
+
`fled` is only aware of files it scanned. It will not warn for overwrites,
|
31
|
+
nor use temporary files in those cases, etc.
|
32
|
+
|
33
|
+
`fled`'s editing model is rather complex and fuzzy. While there are some test
|
34
|
+
cases defined, any help is much appreciated.
|
35
|
+
|
36
|
+
You should be scared when using `fled`.
|
37
|
+
|
38
|
+
### Examples
|
39
|
+
|
40
|
+
Print help text and option list
|
41
|
+
|
42
|
+
fled --help
|
43
|
+
|
44
|
+
Edit current folder
|
45
|
+
|
46
|
+
fled
|
47
|
+
|
48
|
+
Edit all files directly in `path` folder
|
49
|
+
|
50
|
+
fled -a path -d 0
|
51
|
+
|
52
|
+
Save default options
|
53
|
+
|
54
|
+
fled --options > fled.config.yaml
|
55
|
+
|
56
|
+
Edit current folder using options
|
57
|
+
|
58
|
+
fled --load fled.config.yaml
|
59
|
+
|
60
|
+
Add options to a command (`mkdir`, `mv`, `rm` or `rmdir`)
|
61
|
+
|
62
|
+
fled | sed 's/^mv/mv -i/'
|
63
|
+
|
64
|
+
## Listing Format
|
65
|
+
|
66
|
+
folder/ :0
|
67
|
+
file_one :1
|
68
|
+
folder_two/ :2
|
69
|
+
file_three :3
|
70
|
+
|
71
|
+
Each line of the listing is in the format *`[indentation]`* *`[name]`* `:`*`[uid]`*
|
72
|
+
|
73
|
+
- The *indentation* must consist of only spaces, and is used to indicate the parent folder
|
74
|
+
- The *name* must not use colons (`:`). If it is cleared, it is assumed the file/folder is to be deleted
|
75
|
+
- The *uid* is used by FlEd to recognise the original of the edited line. Do not assume a *uid* does not
|
76
|
+
change between runs. It is valid only once.
|
77
|
+
|
78
|
+
## Operations
|
79
|
+
### Creating a new folder
|
80
|
+
|
81
|
+
Add a new line (therefore with no uid):
|
82
|
+
|
83
|
+
folder/ :0
|
84
|
+
new_folder
|
85
|
+
folder_two/ :2
|
86
|
+
|
87
|
+
Generates:
|
88
|
+
|
89
|
+
mkdir folder/new_folder
|
90
|
+
|
91
|
+
### Moving
|
92
|
+
|
93
|
+
Change the indentation and/or line order to change the parent of a file or folder:
|
94
|
+
|
95
|
+
folder/ :0
|
96
|
+
folder_two/ :2
|
97
|
+
file_one :1
|
98
|
+
file_three :3
|
99
|
+
|
100
|
+
Generates:
|
101
|
+
|
102
|
+
mv folder/file_one folder/folder_two/file_one
|
103
|
+
|
104
|
+
*Moving an item below itself or its children is not recommended, as the listing may not be exhaustive*
|
105
|
+
|
106
|
+
### Renaming
|
107
|
+
|
108
|
+
Edit the name while preserving the uid to rename the item
|
109
|
+
|
110
|
+
folder_renamed/ :0
|
111
|
+
file_one :1
|
112
|
+
folder_two/ :2
|
113
|
+
file_changed :3
|
114
|
+
|
115
|
+
Generates:
|
116
|
+
|
117
|
+
mv folder folder_renamed
|
118
|
+
mv folder_renamed/folder_two/file_three folder_renamed/folder_two/file_changed
|
119
|
+
|
120
|
+
*Swapping file names may not work in cases where the generated intermediary file exists but was not included in the listing*
|
121
|
+
|
122
|
+
### Deleting
|
123
|
+
|
124
|
+
Clear a name but leave the uid to delete that item
|
125
|
+
|
126
|
+
folder_renamed/ :0
|
127
|
+
:1
|
128
|
+
:2
|
129
|
+
:3
|
130
|
+
|
131
|
+
Generates:
|
132
|
+
|
133
|
+
mv folder folder_renamed
|
134
|
+
rm folder_renamed/folder_two/file_three
|
135
|
+
rm folder_renamed/file_one
|
136
|
+
rmdir folder_renamed/folder_two
|
137
|
+
|
138
|
+
### No-op
|
139
|
+
|
140
|
+
If a line (and all child-lines) is removed from the listing, it will have no operation.
|
141
|
+
|
142
|
+
folder/ :0
|
143
|
+
|
144
|
+
Generates:
|
145
|
+
*No operation*
|
146
|
+
|
147
|
+
*Note that removing a folder without removing its children will move its children:*
|
148
|
+
|
149
|
+
folder/ :0
|
150
|
+
file_one :1
|
151
|
+
file_three :3
|
152
|
+
|
153
|
+
Generates:
|
154
|
+
|
155
|
+
mv folder/folder_two/file_three folder/file_three
|
156
|
+
|
157
|
+
|
158
|
+
If an indent is forgotten:
|
159
|
+
|
160
|
+
folder/ :0
|
161
|
+
file_one :1
|
162
|
+
file_three :3
|
163
|
+
|
164
|
+
Generates:
|
165
|
+
|
166
|
+
mv folder/folder_two/file_three folder/file_one/file_three
|
167
|
+
|
168
|
+
### All together
|
169
|
+
|
170
|
+
folder_new/ :0
|
171
|
+
new_folder/
|
172
|
+
first :1
|
173
|
+
second :3
|
174
|
+
:2
|
175
|
+
|
176
|
+
Generates:
|
177
|
+
|
178
|
+
mv folder folder_new
|
179
|
+
mkdir folder_new/new_folder
|
180
|
+
mv folder_new/file_one folder_new/new_folder/first
|
181
|
+
mv folder_new/folder_two/file_three folder_new/new_folder/second
|
182
|
+
rmdir folder_new/folder_two
|
183
|
+
|
184
|
+
## Edge cases
|
185
|
+
|
186
|
+
These sort-of work, but are still rather experimental
|
187
|
+
|
188
|
+
### Swapping files
|
189
|
+
|
190
|
+
folder/ :0
|
191
|
+
file_one :1
|
192
|
+
file_two :2
|
193
|
+
|
194
|
+
When applying
|
195
|
+
|
196
|
+
folder/ :0
|
197
|
+
file_two :1
|
198
|
+
file_one :2
|
199
|
+
|
200
|
+
Generates:
|
201
|
+
|
202
|
+
mv folder/file_two folder/file_one.tmp
|
203
|
+
mv folder/file_one folder/file_two
|
204
|
+
mv folder/file_one.tmp folder/file_one
|
205
|
+
|
206
|
+
### Tree swapping
|
207
|
+
|
208
|
+
folder/ :0
|
209
|
+
sub_folder/ :1
|
210
|
+
sub_sub_folder/ :2
|
211
|
+
file.txt :3
|
212
|
+
|
213
|
+
When applying
|
214
|
+
|
215
|
+
sub_sub_folder/ :2
|
216
|
+
sub_folder/ :1
|
217
|
+
folder/ :0
|
218
|
+
file.txt :3
|
219
|
+
|
220
|
+
Generates:
|
221
|
+
|
222
|
+
mv folder/sub_folder/sub_sub_folder sub_sub_folder
|
223
|
+
mv folder/sub_folder sub_sub_folder/sub_folder
|
224
|
+
mv folder sub_sub_folder/sub_folder/folder
|
225
|
+
mv sub_sub_folder/file.txt sub_sub_folder/sub_folder/folder/file.txt
|
226
|
+
|
227
|
+
## Contributors
|
228
|
+
|
229
|
+
- [Eric Doughty-Papassideris](http://github.com/ddlsmurf)
|
230
|
+
|
data/bin/fled
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'yaml'
|
3
|
+
require 'optparse'
|
4
|
+
require 'shellwords'
|
5
|
+
begin
|
6
|
+
require 'fled'
|
7
|
+
rescue LoadError => e
|
8
|
+
$:.unshift File.join(File.dirname(__FILE__),'../lib')
|
9
|
+
require 'fled'
|
10
|
+
end
|
11
|
+
|
12
|
+
class App
|
13
|
+
DEFAULT_SKIP_FOLDERS_RX = ['^\..*', '.*\.app$']
|
14
|
+
DEFAULT_SKIP_FOLDERS = ['.svn', '_svn', '.git', 'CVS', '.hg']
|
15
|
+
DEFAULT_SKIP_FILES_RX = ['^\..*', '.*~$']
|
16
|
+
DEFAULT_SKIP_FILES = [".DS_Store", "Thumbs.db", "Temporary Items"]
|
17
|
+
|
18
|
+
attr_reader :options
|
19
|
+
|
20
|
+
def initialize(arguments = ARGV)
|
21
|
+
@arguments = arguments.dup
|
22
|
+
@options = {}
|
23
|
+
@options[:verbose] = false
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
if parsed_options?
|
28
|
+
if options[:output_options]
|
29
|
+
output_options
|
30
|
+
exit 0
|
31
|
+
end
|
32
|
+
output_version if @options[:verbose]
|
33
|
+
$stderr.puts "Starting on path #{@base_path} at #{DateTime.now}" if @options[:verbose]
|
34
|
+
builder = FlEd::ListingBuilder.new()
|
35
|
+
filter = DTC::Utils::FilteringFileVisitor.new builder, @options
|
36
|
+
DTC::Utils::FileVisitor.browse @base_path, filter
|
37
|
+
content = builder.listing.to_s
|
38
|
+
if content == ""
|
39
|
+
$stderr.puts "No files/folders found"
|
40
|
+
exit 1
|
41
|
+
end
|
42
|
+
result = DTC::Utils::InteractiveEditor::edit(content, ".yaml")
|
43
|
+
if result.nil? || result.strip == "" || result == content
|
44
|
+
$stderr.puts "No changes - aborting" ; exit 2
|
45
|
+
end
|
46
|
+
target_listing = FlEd::FileListing.parse(result)
|
47
|
+
ops = target_listing.operations_from!(builder.listing)
|
48
|
+
if ops.empty?
|
49
|
+
$stderr.puts "No changes - aborting" ; exit 2
|
50
|
+
end
|
51
|
+
ops = [[:pushd, @base_path]] + ops + [[:popd]] unless @options[:no_pushd]
|
52
|
+
puts FlEd::operation_list_to_bash(ops).join("\n")
|
53
|
+
$stderr.puts "\nFinished at #{DateTime.now}" if @options[:verbose]
|
54
|
+
else
|
55
|
+
output_usage
|
56
|
+
end
|
57
|
+
rescue SystemExit => e
|
58
|
+
raise
|
59
|
+
rescue Exception => e
|
60
|
+
puts e
|
61
|
+
output_usage
|
62
|
+
exit 127
|
63
|
+
end
|
64
|
+
protected
|
65
|
+
|
66
|
+
def self.default_options
|
67
|
+
({
|
68
|
+
:max_depth => -1,
|
69
|
+
:excluded_files =>
|
70
|
+
DEFAULT_SKIP_FILES_RX +
|
71
|
+
DEFAULT_SKIP_FILES.map { |e| '\A' + Regexp.escape(e) + '\z' },
|
72
|
+
:excluded_directories =>
|
73
|
+
DEFAULT_SKIP_FOLDERS_RX +
|
74
|
+
DEFAULT_SKIP_FOLDERS.map { |e| '\A' + Regexp.escape(e) + '\z' },
|
75
|
+
})
|
76
|
+
end
|
77
|
+
|
78
|
+
def parsed_options?
|
79
|
+
options = {}
|
80
|
+
@optionparser = OptionParser.new do |opts|
|
81
|
+
opts.banner = "Usage: #{$0} [options] [base_path]"
|
82
|
+
opts.separator ""
|
83
|
+
opts.separator "Selection"
|
84
|
+
|
85
|
+
opts.on('-i', '--include RX', String, "Only include files and folders that match any such regexp") { |rx| (options[:included] ||= []) << rx }
|
86
|
+
opts.on('--include-dirs RX', String, "Only include folders that match any such regexp") { |rx| (options[:included_directories] ||= []) << rx }
|
87
|
+
opts.on('--include-files RX', String, "Only include files that match any such regexp") { |rx| (options[:included_files] ||= []) << rx }
|
88
|
+
opts.on('-x', '--exclude RX', String, "Exclude files and folders that match any such regexp") { |rx| (options[:excluded] ||= []) << rx }
|
89
|
+
opts.on('--exclude-dirs RX', String, "Exclude folders that match any such regexp") { |rx| (options[:excluded_directories] ||= []) << rx }
|
90
|
+
opts.on('--exclude-files RX', String, "Exclude files that match any such regexp") { |rx| (options[:excluded_files] ||= []) << rx }
|
91
|
+
opts.on('-a', '--no-exclude', "Empties all the lists of exclusions") do
|
92
|
+
options[:excluded] = []
|
93
|
+
options[:excluded_files] = []
|
94
|
+
options[:excluded_directories] = []
|
95
|
+
end
|
96
|
+
opts.on('-r', '--recursive', "Scan directories recursively") { options[:max_depth] ||= -1 }
|
97
|
+
opts.on("-d", "--depth N", Integer, "Set maximum recursion to N subfolders. (0=no recursion)") { |n| options[:max_depth] = n.to_i }
|
98
|
+
|
99
|
+
opts.separator ""
|
100
|
+
opts.separator "Script"
|
101
|
+
opts.on('--no-pushd', "Do not include pushd/popd pair in script") { options[:no_pushd] = true }
|
102
|
+
|
103
|
+
opts.separator ""
|
104
|
+
opts.separator "General"
|
105
|
+
opts.on_tail('-l', '--load PATH.YAML', "Merge in the specified yaml files options") { |file|
|
106
|
+
File.open(file) { |file| options = options.merge(YAML.load(file)) }
|
107
|
+
}
|
108
|
+
opts.on_tail('--options', "Show options as interpreted and exit without doing anything") { options[:output_options] = true }
|
109
|
+
opts.on('-v', '--verbose', "Display more information about what the tool is doing...") { options[:verbose] = true }
|
110
|
+
opts.on_tail('--version', "Show version of this tool") { output_version ; exit 0 }
|
111
|
+
opts.on_tail('-h', '--help', "Show this help message") { output_help ; exit 0 }
|
112
|
+
end
|
113
|
+
@optionparser.parse!(@arguments)
|
114
|
+
@base_path = File.expand_path(@arguments.first || options[:base_path] || ".")
|
115
|
+
raise RuntimeError, "No more than one argument should be present" if @arguments.count > 1
|
116
|
+
@options = self.class.default_options.merge(options)
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def output_options out = $stdout
|
121
|
+
opts = @options.dup
|
122
|
+
opts.delete(:output_options)
|
123
|
+
out.puts opts.to_yaml
|
124
|
+
end
|
125
|
+
|
126
|
+
def output_help
|
127
|
+
output_version
|
128
|
+
$stderr.puts ""
|
129
|
+
$stderr.puts " Disclaimer: The author is not responsible for anything related"
|
130
|
+
$stderr.puts " to this tool. This is quite powerful and slight mistakes can"
|
131
|
+
$stderr.puts " lead to loss of data or worse. The author recommends you not"
|
132
|
+
$stderr.puts " use this."
|
133
|
+
$stderr.puts ""
|
134
|
+
$stderr.puts " Operation:"
|
135
|
+
$stderr.puts " - Generate list of files and folder"
|
136
|
+
$stderr.puts " - Open in your favorite ($EDITOR) text editor"
|
137
|
+
$stderr.puts " - You edit file names in the editor, then save and close"
|
138
|
+
$stderr.puts " - The file list is reloaded and compared to the original"
|
139
|
+
$stderr.puts " - A shell script to re-organise the files/folders is printed"
|
140
|
+
$stderr.puts ""
|
141
|
+
output_usage
|
142
|
+
end
|
143
|
+
|
144
|
+
def output_usage
|
145
|
+
$stderr.puts @optionparser
|
146
|
+
end
|
147
|
+
|
148
|
+
def output_version
|
149
|
+
$stderr.puts "#{File.basename(__FILE__)} version #{VERSION}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
App.new.run
|
@@ -0,0 +1,259 @@
|
|
1
|
+
module DTC
|
2
|
+
module Utils
|
3
|
+
module DSLDSL
|
4
|
+
class DSLVisitor
|
5
|
+
# very approximate visitor =)
|
6
|
+
def enter key, *args ; end
|
7
|
+
def leave ; end
|
8
|
+
def add key, *args ; end
|
9
|
+
def visit_dsl &blk
|
10
|
+
builder = self.class.delegate_klass.new(self)
|
11
|
+
self.class.context_klass.new(builder).__instance_exec(&blk)
|
12
|
+
builder.flush
|
13
|
+
end
|
14
|
+
protected
|
15
|
+
def self.delegate_klass ; StaticTreeDSLDelegate ; end
|
16
|
+
def self.context_klass ; StaticTreeDSLContextBlank ; end
|
17
|
+
end
|
18
|
+
|
19
|
+
class DSLArrayWriter < DSLVisitor
|
20
|
+
attr_reader :stack
|
21
|
+
def initialize
|
22
|
+
@stack = [[]]
|
23
|
+
end
|
24
|
+
def enter sym, *args
|
25
|
+
@stack.push [[sym, *args]]
|
26
|
+
end
|
27
|
+
def leave
|
28
|
+
@stack.pop
|
29
|
+
end
|
30
|
+
def add sym, *args
|
31
|
+
@stack.last << [sym, *args]
|
32
|
+
end
|
33
|
+
def each &blk
|
34
|
+
return enum_for(:each) unless block_given?
|
35
|
+
each_call_of @stack.first, &blk
|
36
|
+
end
|
37
|
+
protected
|
38
|
+
def each_call_of parent, &block
|
39
|
+
parent.each do |item|
|
40
|
+
if item.first.is_a?(Symbol)
|
41
|
+
yield item
|
42
|
+
else
|
43
|
+
each_call_of item, &block
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class DSLHashWriter < DSLVisitor
|
50
|
+
def initialize target
|
51
|
+
@stack = [target]
|
52
|
+
end
|
53
|
+
def enter key, *args
|
54
|
+
container = add_container(key, *args)
|
55
|
+
@stack.push(container) if container
|
56
|
+
container
|
57
|
+
end
|
58
|
+
def leave
|
59
|
+
@stack.pop
|
60
|
+
end
|
61
|
+
def add key, *args
|
62
|
+
add_key key, args
|
63
|
+
end
|
64
|
+
def self.write_static_tree_dsl target = {}, &blk
|
65
|
+
visitor = self.new(result = target)
|
66
|
+
visitor.visit_dsl(&blk)
|
67
|
+
result
|
68
|
+
end
|
69
|
+
def add_container key, *args
|
70
|
+
container = {}
|
71
|
+
container[:options] = args unless args.empty?
|
72
|
+
add_key key, container
|
73
|
+
end
|
74
|
+
def add_key key, val
|
75
|
+
container = @stack.last
|
76
|
+
key = key.to_s
|
77
|
+
raise RuntimeError, "Key #{key.inspect} already defined" if container[key]
|
78
|
+
container[key] = val
|
79
|
+
val
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class StaticTreeDSLDelegate
|
84
|
+
def initialize visitor, prefix = nil
|
85
|
+
@visitor = visitor
|
86
|
+
@prefix = prefix
|
87
|
+
@pending_prefix = nil
|
88
|
+
@called = false
|
89
|
+
end
|
90
|
+
def prefix sym
|
91
|
+
@called = true
|
92
|
+
flush
|
93
|
+
@pending_prefix = self.class.new(@visitor, with_prefix(sym))
|
94
|
+
end
|
95
|
+
def flush
|
96
|
+
if @pending_prefix
|
97
|
+
@pending_prefix.add_unless_called
|
98
|
+
@pending_prefix = nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
def add sym, *args
|
102
|
+
@called = true
|
103
|
+
@visitor.add(with_prefix(sym), *args)
|
104
|
+
end
|
105
|
+
def enter sym, *args
|
106
|
+
flush
|
107
|
+
@called = true
|
108
|
+
@visitor.enter(with_prefix(sym), *args)
|
109
|
+
end
|
110
|
+
def leave
|
111
|
+
flush
|
112
|
+
@visitor.leave
|
113
|
+
end
|
114
|
+
protected
|
115
|
+
def with_prefix sym
|
116
|
+
@prefix ? (sym ? "#{@prefix}.#{sym}".to_sym : @prefix) : sym
|
117
|
+
end
|
118
|
+
def add_unless_called
|
119
|
+
flush
|
120
|
+
@visitor.add(@prefix) unless @called
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class StaticTreeDSLContextBlank
|
125
|
+
alias_method :__instance_exec, :instance_exec
|
126
|
+
alias_method :__class, :class
|
127
|
+
instance_methods.each { |meth| undef_method(meth) unless meth =~ /\A__/ || meth == :object_id }
|
128
|
+
def initialize delegate, unprefixed = delegate
|
129
|
+
@delegate = delegate
|
130
|
+
@unprefixed = unprefixed
|
131
|
+
end
|
132
|
+
def method_missing(meth, *args, &block)
|
133
|
+
if block
|
134
|
+
@delegate.enter meth, *args
|
135
|
+
__class.new(@unprefixed, @unprefixed).__instance_exec(&block)
|
136
|
+
@unprefixed.flush
|
137
|
+
@delegate.leave
|
138
|
+
else
|
139
|
+
if args.empty?
|
140
|
+
return __class.new(@delegate.prefix(meth), @unprefixed)
|
141
|
+
else
|
142
|
+
@delegate.add(meth, *args)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
self
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
if __FILE__ == $0
|
150
|
+
|
151
|
+
class DebugStaticTreeDSLDelegate < StaticTreeDSLDelegate
|
152
|
+
def prefix sym
|
153
|
+
p [:prefix, sym, @prefix]
|
154
|
+
super
|
155
|
+
end
|
156
|
+
def flush
|
157
|
+
p [:flush, @prefix] if @pending_prefix
|
158
|
+
super
|
159
|
+
end
|
160
|
+
def add sym, *args
|
161
|
+
p [:add, sym, @prefix]
|
162
|
+
super
|
163
|
+
end
|
164
|
+
def enter sym, *args
|
165
|
+
p [:enter, sym, @prefix]
|
166
|
+
super
|
167
|
+
end
|
168
|
+
def leave
|
169
|
+
p [:leave, @prefix]
|
170
|
+
super
|
171
|
+
end
|
172
|
+
def add_unless_called
|
173
|
+
p [:flush_self, @prefix] unless @called
|
174
|
+
super
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class DebugDSLHashWriter < DSLHashWriter
|
179
|
+
def enter sym, *args
|
180
|
+
p [:wenter, sym]
|
181
|
+
super
|
182
|
+
end
|
183
|
+
def leave
|
184
|
+
p [:wleave]
|
185
|
+
super
|
186
|
+
end
|
187
|
+
def add sym, *args
|
188
|
+
p [:wadd, sym, args]
|
189
|
+
super
|
190
|
+
end
|
191
|
+
def self.delegate_klass ; DebugStaticTreeDSLDelegate ; end
|
192
|
+
end
|
193
|
+
|
194
|
+
module Examples
|
195
|
+
module Simple
|
196
|
+
result = DSLHashWriter.write_static_tree_dsl do
|
197
|
+
fichier "value"
|
198
|
+
fichier2.txt "one", "two"
|
199
|
+
end
|
200
|
+
result # => {"fichier"=>["value"], "fichier2.txt"=>["one", "two"]}
|
201
|
+
|
202
|
+
result = DSLHashWriter.write_static_tree_dsl do
|
203
|
+
dossier {
|
204
|
+
sous.dossier "valeur" do
|
205
|
+
file.txt
|
206
|
+
end
|
207
|
+
}
|
208
|
+
end
|
209
|
+
result # => {"dossier"=>{"sous.dossier"=>{:options=>["valeur"], "file.txt"=>[]}}}
|
210
|
+
|
211
|
+
result = DSLHashWriter.write_static_tree_dsl do
|
212
|
+
%w[hdpi mdpi ldpi].each do |resolution|
|
213
|
+
image.__send__(resolution.to_sym).png
|
214
|
+
end
|
215
|
+
end
|
216
|
+
result # => {"image.ldpi.png"=>[], "image.hdpi.png"=>[], "image.mdpi.png"=>[]}
|
217
|
+
|
218
|
+
result = DSLHashWriter.write_static_tree_dsl do
|
219
|
+
user {
|
220
|
+
name :string
|
221
|
+
email :string
|
222
|
+
address(:json) {
|
223
|
+
line :string
|
224
|
+
country
|
225
|
+
}
|
226
|
+
}
|
227
|
+
end
|
228
|
+
result # => {"user"=>{"address"=>{"country"=>[], "line"=>[:string], :options=>[:json]}, "name"=>[:string], "email"=>[:string]}}
|
229
|
+
|
230
|
+
result = DSLHashWriter.write_static_tree_dsl do
|
231
|
+
def base_file
|
232
|
+
yes.this.is.base
|
233
|
+
end
|
234
|
+
base_file.txt
|
235
|
+
base_file.png
|
236
|
+
end
|
237
|
+
result # => {"yes.this.is.base.png"=>[], "yes.this.is.base.txt"=>[]}
|
238
|
+
end
|
239
|
+
module CustomBuilder
|
240
|
+
class EscapableStaticTreeDSLContextBlank < StaticTreeDSLContextBlank
|
241
|
+
def method_missing(meth, *args, &block)
|
242
|
+
meth = args.shift if meth == :raw
|
243
|
+
super(meth, *args, &block)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
class EscapableDSLHashWriter < DSLHashWriter
|
247
|
+
def self.context_klass ; EscapableStaticTreeDSLContextBlank ; end
|
248
|
+
end
|
249
|
+
result = EscapableDSLHashWriter.write_static_tree_dsl do
|
250
|
+
3.times { |i| raw(i).png }
|
251
|
+
image.raw("%!") { file }
|
252
|
+
end
|
253
|
+
result # => {"0.png"=>[], "1.png"=>[], "image.%!"=>{"file"=>[]}, "2.png"=>[]}
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|