quik 0.1.1 → 1.0.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.
- checksums.yaml +5 -5
- data/{HISTORY.md → CHANGELOG.md} +3 -3
- data/LICENSE.md +116 -0
- data/Manifest.txt +30 -24
- data/README.md +133 -106
- data/Rakefile +33 -34
- data/bin/qk +5 -5
- data/bin/quik +5 -5
- data/lib/quik.rb +55 -43
- data/lib/quik/builder.rb +96 -94
- data/lib/quik/catalog.rb +73 -0
- data/lib/quik/cli/main.rb +213 -150
- data/lib/quik/cli/opts.rb +33 -33
- data/lib/quik/colors.rb +148 -0
- data/lib/quik/config.rb +60 -60
- data/lib/quik/merger.rb +207 -202
- data/lib/quik/package.rb +116 -99
- data/lib/quik/version.rb +24 -24
- data/lib/quik/wizard.rb +88 -88
- data/test/data/gem-starter-template/Manifest.txt +9 -9
- data/test/data/gem-starter-template/README.md +22 -22
- data/test/data/gem-starter-template/lib/__filename__/version.rb +12 -12
- data/test/helper.rb +18 -22
- data/test/test_colors.rb +39 -0
- data/test/test_config.rb +35 -0
- data/test/test_merger.rb +35 -35
- data/test/test_package.rb +22 -22
- data/test/test_wizard.rb +50 -0
- metadata +31 -35
- data/.gemtest +0 -0
data/lib/quik/cli/opts.rb
CHANGED
@@ -1,33 +1,33 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Quik
|
4
|
-
|
5
|
-
class Opts
|
6
|
-
|
7
|
-
def merge_gli_options!( options = {} )
|
8
|
-
@test = true if options[:test] == true
|
9
|
-
@verbose = true if options[:verbose] == true
|
10
|
-
end
|
11
|
-
|
12
|
-
|
13
|
-
def verbose=(boolean) # add: alias for debug ??
|
14
|
-
@verbose = boolean
|
15
|
-
end
|
16
|
-
|
17
|
-
def verbose?
|
18
|
-
return false if @verbose.nil? # default verbose/debug flag is false
|
19
|
-
@verbose == true
|
20
|
-
end
|
21
|
-
|
22
|
-
def test=(boolean)
|
23
|
-
@test = boolean
|
24
|
-
end
|
25
|
-
|
26
|
-
def test?
|
27
|
-
return false if @test.nil? # default test/dry-run flag is false
|
28
|
-
@test == true
|
29
|
-
end
|
30
|
-
|
31
|
-
end # class Opts
|
32
|
-
|
33
|
-
end # module Quik
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Quik
|
4
|
+
|
5
|
+
class Opts
|
6
|
+
|
7
|
+
def merge_gli_options!( options = {} )
|
8
|
+
@test = true if options[:test] == true
|
9
|
+
@verbose = true if options[:verbose] == true
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def verbose=(boolean) # add: alias for debug ??
|
14
|
+
@verbose = boolean
|
15
|
+
end
|
16
|
+
|
17
|
+
def verbose?
|
18
|
+
return false if @verbose.nil? # default verbose/debug flag is false
|
19
|
+
@verbose == true
|
20
|
+
end
|
21
|
+
|
22
|
+
def test=(boolean)
|
23
|
+
@test = boolean
|
24
|
+
end
|
25
|
+
|
26
|
+
def test?
|
27
|
+
return false if @test.nil? # default test/dry-run flag is false
|
28
|
+
@test == true
|
29
|
+
end
|
30
|
+
|
31
|
+
end # class Opts
|
32
|
+
|
33
|
+
end # module Quik
|
data/lib/quik/colors.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
### todo: move to textutils for (re)use - why? why not??
|
4
|
+
### todo add some references to alternative color gems
|
5
|
+
##
|
6
|
+
## e.g. https://github.com/fazibear/colorize
|
7
|
+
## http://github.com/ssoroka/ansi
|
8
|
+
## http://flori.github.com/term-ansicolor/
|
9
|
+
## http://github.com/sickill/rainbow
|
10
|
+
## https://github.com/janlelis/paint
|
11
|
+
|
12
|
+
|
13
|
+
## move to core_ext.rb - why? why not??
|
14
|
+
|
15
|
+
###
|
16
|
+
## fix: use css style
|
17
|
+
## e.g. color( :red )
|
18
|
+
## color( :blue )
|
19
|
+
## background_color( :red )
|
20
|
+
## font_style( :bold ) -- why? why not??
|
21
|
+
|
22
|
+
class String
|
23
|
+
|
24
|
+
def self.use_colors?
|
25
|
+
@@use_color ||= true
|
26
|
+
## todo/fix: check for windows on load (set to false;otherwise true)
|
27
|
+
@@use_color
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.use_colors=(value)
|
31
|
+
@@use_color = value
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def red() colorize(31); end
|
36
|
+
def green() colorize(32); end
|
37
|
+
def yellow() colorize(33); end ## note: more brown-ish (if not used w/ bold/bright mode) ??
|
38
|
+
def blue() colorize(34); end
|
39
|
+
def magenta() colorize(35); end ## pink ??
|
40
|
+
def cyan() colorize(36); end ## light blue ??
|
41
|
+
|
42
|
+
def bold() colorize(1); end ## just use 0 clear for now; instead of BOLD_OFF (22) code; use bright as an alias??
|
43
|
+
|
44
|
+
private
|
45
|
+
def colorize(code)
|
46
|
+
if self.class.use_colors?
|
47
|
+
"\e[#{code}m#{self}\e[0m"
|
48
|
+
else
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
## todo - add modes e.g. bold/underscore/etc.
|
55
|
+
=begin
|
56
|
+
class String
|
57
|
+
def black; "\e[30m#{self}\e[0m" end
|
58
|
+
def red; "\e[31m#{self}\e[0m" end
|
59
|
+
def green; "\e[32m#{self}\e[0m" end
|
60
|
+
def brown; "\e[33m#{self}\e[0m" end
|
61
|
+
def blue; "\e[34m#{self}\e[0m" end
|
62
|
+
def magenta; "\e[35m#{self}\e[0m" end
|
63
|
+
def cyan; "\e[36m#{self}\e[0m" end
|
64
|
+
def gray; "\e[37m#{self}\e[0m" end
|
65
|
+
|
66
|
+
def on_black; "\e[40m#{self}\e[0m" end
|
67
|
+
def on_red; "\e[41m#{self}\e[0m" end
|
68
|
+
def on_green; "\e[42m#{self}\e[0m" end
|
69
|
+
def on_brown; "\e[43m#{self}\e[0m" end
|
70
|
+
def on_blue; "\e[44m#{self}\e[0m" end
|
71
|
+
def on_magenta; "\e[45m#{self}\e[0m" end
|
72
|
+
def on_cyan; "\e[46m#{self}\e[0m" end
|
73
|
+
def on_gray; "\e[47m#{self}\e[0m" end
|
74
|
+
|
75
|
+
def bold; "\e[1m#{self}\e[21m" end
|
76
|
+
def italic; "\e[3m#{self}\e[23m" end
|
77
|
+
def underline; "\e[4m#{self}\e[24m" end
|
78
|
+
def blink; "\e[5m#{self}\e[25m" end
|
79
|
+
def reverse_color; "\e[7m#{self}\e[27m" end
|
80
|
+
|
81
|
+
use Ansi constants - why? why not? e.g.:
|
82
|
+
//foreground color
|
83
|
+
public static final String BLACK_TEXT() { return "\033[30m";}
|
84
|
+
public static final String RED_TEXT() { return "\033[31m";}
|
85
|
+
public static final String GREEN_TEXT() { return "\033[32m";}
|
86
|
+
public static final String BROWN_TEXT() { return "\033[33m";}
|
87
|
+
public static final String BLUE_TEXT() { return "\033[34m";}
|
88
|
+
public static final String MAGENTA_TEXT() { return "\033[35m";}
|
89
|
+
public static final String CYAN_TEXT() { return "\033[36m";}
|
90
|
+
public static final String GRAY_TEXT() { return "\033[37m";}
|
91
|
+
|
92
|
+
//background color
|
93
|
+
public static final String BLACK_BACK() { return "\033[40m";}
|
94
|
+
public static final String RED_BACK() { return "\033[41m";}
|
95
|
+
public static final String GREEN_BACK() { return "\033[42m";}
|
96
|
+
public static final String BROWN_BACK() { return "\033[43m";}
|
97
|
+
public static final String BLUE_BACK() { return "\033[44m";}
|
98
|
+
public static final String MAGENTA_BACK() { return "\033[45m";}
|
99
|
+
public static final String CYAN_BACK() { return "\033[46m";}
|
100
|
+
public static final String WHITE_BACK() { return "\033[47m";}
|
101
|
+
|
102
|
+
//ANSI control chars
|
103
|
+
public static final String RESET_COLORS() { return "\033[0m";}
|
104
|
+
public static final String BOLD_ON() { return "\033[1m";}
|
105
|
+
public static final String BLINK_ON() { return "\033[5m";}
|
106
|
+
public static final String REVERSE_ON() { return "\033[7m";}
|
107
|
+
public static final String BOLD_OFF() { return "\033[22m";}
|
108
|
+
public static final String BLINK_OFF() { return "\033[25m";}
|
109
|
+
public static final String REVERSE_OFF() { return "\033[27m";}
|
110
|
+
end
|
111
|
+
|
112
|
+
Code Effect
|
113
|
+
0 Turn off all attributes
|
114
|
+
1 Set bright mode
|
115
|
+
4 Set underline mode
|
116
|
+
5 Set blink mode
|
117
|
+
7 Exchange foreground and background colors
|
118
|
+
8 Hide text (foreground color would be the same as background)
|
119
|
+
30 Black text
|
120
|
+
31 Red text
|
121
|
+
32 Green text
|
122
|
+
33 Yellow text
|
123
|
+
34 Blue text
|
124
|
+
35 Magenta text
|
125
|
+
36 Cyan text
|
126
|
+
37 White text
|
127
|
+
39 Default text color
|
128
|
+
40 Black background
|
129
|
+
41 Red background
|
130
|
+
42 Green background
|
131
|
+
43 Yellow background
|
132
|
+
44 Blue background
|
133
|
+
45 Magenta background
|
134
|
+
46 Cyan background
|
135
|
+
47 White background
|
136
|
+
49 Default background color
|
137
|
+
|
138
|
+
note:
|
139
|
+
puts "\e[31m" # set format (red foreground)
|
140
|
+
puts "\e[0" # clear format
|
141
|
+
puts "green-#{"red".red}-green".green # will be green-red-normal, because of \e[0
|
142
|
+
e.g. for now colors can NOT get nested
|
143
|
+
plus if you bold/italic/etc. use it before the color e.g.
|
144
|
+
bold.red etc.
|
145
|
+
=end
|
146
|
+
|
147
|
+
|
148
|
+
end # class String
|
data/lib/quik/config.rb
CHANGED
@@ -1,60 +1,60 @@
|
|
1
|
-
#encoding: utf-8
|
2
|
-
|
3
|
-
module Quik
|
4
|
-
|
5
|
-
##
|
6
|
-
# used for config block
|
7
|
-
# lets you access props (even nested) that don't yet exist
|
8
|
-
# and all props get stored in a hash
|
9
|
-
#
|
10
|
-
# e.g
|
11
|
-
# c = OpenConfig.new
|
12
|
-
# c.title = 'title'
|
13
|
-
# c.author.name = 'name'
|
14
|
-
|
15
|
-
# c.quik.last_updated = Time.now
|
16
|
-
# c.quik.title = 'title'
|
17
|
-
# c.quik.name = 'name'
|
18
|
-
# c.quik.theme = 'theme'
|
19
|
-
|
20
|
-
class OpenConfig
|
21
|
-
|
22
|
-
def initialize
|
23
|
-
@h = {}
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_h
|
27
|
-
h = {}
|
28
|
-
@h.each do |k,v|
|
29
|
-
if v.is_a? OpenConfig
|
30
|
-
h[ k ] = v.to_h
|
31
|
-
else
|
32
|
-
h[ k ] = v ## just pass along as is
|
33
|
-
end
|
34
|
-
end
|
35
|
-
h
|
36
|
-
end
|
37
|
-
|
38
|
-
def method_missing( m, *args, &block)
|
39
|
-
if m.to_s =~ /^(.*)=$/ ## setter
|
40
|
-
puts "config lookup (setter) >#{m}< #{m.class.name}, #{args.inspect}"
|
41
|
-
key = m[0..-2].to_s ## cut off trailing =
|
42
|
-
@h[ key ] = args[0].to_s # note: assume first arg is value for setter
|
43
|
-
# note: for now all values are strings (always use to_s)
|
44
|
-
else ## assume getter
|
45
|
-
## fix: add check for args?? must be 0 for getters??
|
46
|
-
## use else super to delegate non-getters??
|
47
|
-
puts "config lookup (getter) >#{m}< #{m.class.name}"
|
48
|
-
key = m.to_s
|
49
|
-
value = @h[ key ]
|
50
|
-
if value.nil?
|
51
|
-
puts " config add (nested) hash"
|
52
|
-
value = @h[ key ] = OpenConfig.new
|
53
|
-
end
|
54
|
-
value
|
55
|
-
end
|
56
|
-
end # method_missing
|
57
|
-
|
58
|
-
end # class OpenConfig
|
59
|
-
|
60
|
-
end # module Quik
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
module Quik
|
4
|
+
|
5
|
+
##
|
6
|
+
# used for config block
|
7
|
+
# lets you access props (even nested) that don't yet exist
|
8
|
+
# and all props get stored in a hash
|
9
|
+
#
|
10
|
+
# e.g
|
11
|
+
# c = OpenConfig.new
|
12
|
+
# c.title = 'title'
|
13
|
+
# c.author.name = 'name'
|
14
|
+
|
15
|
+
# c.quik.last_updated = Time.now
|
16
|
+
# c.quik.title = 'title'
|
17
|
+
# c.quik.name = 'name'
|
18
|
+
# c.quik.theme = 'theme'
|
19
|
+
|
20
|
+
class OpenConfig
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@h = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
h = {}
|
28
|
+
@h.each do |k,v|
|
29
|
+
if v.is_a? OpenConfig
|
30
|
+
h[ k ] = v.to_h
|
31
|
+
else
|
32
|
+
h[ k ] = v ## just pass along as is
|
33
|
+
end
|
34
|
+
end
|
35
|
+
h
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing( m, *args, &block)
|
39
|
+
if m.to_s =~ /^(.*)=$/ ## setter
|
40
|
+
puts "config lookup (setter) >#{m}< #{m.class.name}, #{args.inspect}"
|
41
|
+
key = m[0..-2].to_s ## cut off trailing =
|
42
|
+
@h[ key ] = args[0].to_s # note: assume first arg is value for setter
|
43
|
+
# note: for now all values are strings (always use to_s)
|
44
|
+
else ## assume getter
|
45
|
+
## fix: add check for args?? must be 0 for getters??
|
46
|
+
## use else super to delegate non-getters??
|
47
|
+
puts "config lookup (getter) >#{m}< #{m.class.name}"
|
48
|
+
key = m.to_s
|
49
|
+
value = @h[ key ]
|
50
|
+
if value.nil?
|
51
|
+
puts " config add (nested) hash"
|
52
|
+
value = @h[ key ] = OpenConfig.new
|
53
|
+
end
|
54
|
+
value
|
55
|
+
end
|
56
|
+
end # method_missing
|
57
|
+
|
58
|
+
end # class OpenConfig
|
59
|
+
|
60
|
+
end # module Quik
|
data/lib/quik/merger.rb
CHANGED
@@ -1,202 +1,207 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Quik
|
4
|
-
|
5
|
-
class Merger
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def find_files( root_dir )
|
10
|
-
tree = []
|
11
|
-
|
12
|
-
files = Dir.entries( root_dir )
|
13
|
-
files = files.sort
|
14
|
-
puts "#{root_dir}:"
|
15
|
-
pp files
|
16
|
-
|
17
|
-
files.each do |file|
|
18
|
-
if File.directory?( "#{root_dir}/#{file}" )
|
19
|
-
## note: skip directory if it starts with dot e.g. . or .. or .git etc.
|
20
|
-
if file.start_with?( '.' )
|
21
|
-
puts "skipping directory >#{file}< (#{root_dir})"
|
22
|
-
next
|
23
|
-
end
|
24
|
-
subtree = find_files( "#{root_dir}/#{file}" )
|
25
|
-
tree << [ file, subtree ]
|
26
|
-
else
|
27
|
-
## just a "regular" file
|
28
|
-
tree << file
|
29
|
-
end
|
30
|
-
end
|
31
|
-
tree
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
def merge_filenames( root_dir, hash, opts={} ) ## flags e.g. noop, verbose, etc. see FileUtils
|
36
|
-
tree = find_files( root_dir )
|
37
|
-
pp tree
|
38
|
-
|
39
|
-
flags = { ## always use verbose mode for now
|
40
|
-
verbose: true
|
41
|
-
}
|
42
|
-
|
43
|
-
## todo/check: move opts={} to initialize e.g. Merger.new( opts={}) why? why not??
|
44
|
-
|
45
|
-
## check for noop e.g. test mode/dry run
|
46
|
-
flags[ :noop ] = true if opts[:test] || opts[:dry_run] || opts[:noop]
|
47
|
-
|
48
|
-
puts "walk tree:"
|
49
|
-
merge_filenames_worker_step1( root_dir, hash, tree, flags )
|
50
|
-
merge_filenames_worker_step2( root_dir, hash, tree, flags )
|
51
|
-
end
|
52
|
-
|
53
|
-
def merge_filenames_worker_step1( root_dir, hash, tree, flags )
|
54
|
-
## note: step 1 move all files
|
55
|
-
## step 2 move all dirs
|
56
|
-
|
57
|
-
tree.each do |node|
|
58
|
-
if node.is_a? Array ## assume it's a directory
|
59
|
-
merge_filenames_worker_step1( "#{root_dir}/#{node[0]}", hash, node[1], flags )
|
60
|
-
else ## assume it's a file
|
61
|
-
old_name = node
|
62
|
-
new_name = merge_path( old_name, hash )
|
63
|
-
if old_name == new_name
|
64
|
-
puts " keep file #{node} (#{root_dir})"
|
65
|
-
else
|
66
|
-
puts " *** move file #{old_name} => #{new_name} (#{root_dir})"
|
67
|
-
src_path = "#{root_dir}/#{old_name}"
|
68
|
-
dest_path = "#{root_dir}/#{new_name}"
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
puts "
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
tree
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
puts "
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
##
|
178
|
-
##
|
179
|
-
|
180
|
-
##
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Quik
|
4
|
+
|
5
|
+
class Merger
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
def find_files( root_dir )
|
10
|
+
tree = []
|
11
|
+
|
12
|
+
files = Dir.entries( root_dir )
|
13
|
+
files = files.sort
|
14
|
+
puts "#{root_dir}:"
|
15
|
+
pp files
|
16
|
+
|
17
|
+
files.each do |file|
|
18
|
+
if File.directory?( "#{root_dir}/#{file}" )
|
19
|
+
## note: skip directory if it starts with dot e.g. . or .. or .git etc.
|
20
|
+
if file.start_with?( '.' )
|
21
|
+
puts "skipping directory >#{file}< (#{root_dir})"
|
22
|
+
next
|
23
|
+
end
|
24
|
+
subtree = find_files( "#{root_dir}/#{file}" )
|
25
|
+
tree << [ file, subtree ]
|
26
|
+
else
|
27
|
+
## just a "regular" file
|
28
|
+
tree << file
|
29
|
+
end
|
30
|
+
end
|
31
|
+
tree
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def merge_filenames( root_dir, hash, opts={} ) ## flags e.g. noop, verbose, etc. see FileUtils
|
36
|
+
tree = find_files( root_dir )
|
37
|
+
pp tree
|
38
|
+
|
39
|
+
flags = { ## always use verbose mode for now
|
40
|
+
verbose: true
|
41
|
+
}
|
42
|
+
|
43
|
+
## todo/check: move opts={} to initialize e.g. Merger.new( opts={}) why? why not??
|
44
|
+
|
45
|
+
## check for noop e.g. test mode/dry run
|
46
|
+
flags[ :noop ] = true if opts[:test] || opts[:dry_run] || opts[:noop]
|
47
|
+
|
48
|
+
puts "walk tree:"
|
49
|
+
merge_filenames_worker_step1( root_dir, hash, tree, flags )
|
50
|
+
merge_filenames_worker_step2( root_dir, hash, tree, flags )
|
51
|
+
end
|
52
|
+
|
53
|
+
def merge_filenames_worker_step1( root_dir, hash, tree, flags )
|
54
|
+
## note: step 1 move all files
|
55
|
+
## step 2 move all dirs
|
56
|
+
|
57
|
+
tree.each do |node|
|
58
|
+
if node.is_a? Array ## assume it's a directory
|
59
|
+
merge_filenames_worker_step1( "#{root_dir}/#{node[0]}", hash, node[1], flags )
|
60
|
+
else ## assume it's a file
|
61
|
+
old_name = node
|
62
|
+
new_name = merge_path( old_name, hash )
|
63
|
+
if old_name == new_name
|
64
|
+
puts " keep file #{node} (#{root_dir})"
|
65
|
+
else
|
66
|
+
puts " *** move file #{old_name} => #{new_name} (#{root_dir})"
|
67
|
+
src_path = "#{root_dir}/#{old_name}"
|
68
|
+
dest_path = "#{root_dir}/#{new_name}"
|
69
|
+
## note: make sure subpath exists (e.g. replacement might include new (sub)dirs too)
|
70
|
+
FileUtils.mkdir_p( File.dirname( dest_path ), flags ) unless Dir.exist?( File.dirname( dest_path ))
|
71
|
+
FileUtils.mv( src_path, dest_path, flags )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def merge_filenames_worker_step2( root_dir, hash, tree, flags )
|
78
|
+
## note: step 1 move all files
|
79
|
+
## step 2 move all dirs
|
80
|
+
|
81
|
+
tree.each do |node|
|
82
|
+
if node.is_a? Array ## assume it's a directory
|
83
|
+
merge_filenames_worker_step2( "#{root_dir}/#{node[0]}", hash, node[1], flags )
|
84
|
+
old_name = node[0]
|
85
|
+
new_name = merge_path( old_name, hash )
|
86
|
+
if old_name == new_name
|
87
|
+
puts " keep dir #{node[0]} (#{root_dir})"
|
88
|
+
else
|
89
|
+
puts " *** move dir #{old_name} => #{new_name} (#{root_dir})"
|
90
|
+
src_path = "#{root_dir}/#{old_name}"
|
91
|
+
dest_path = "#{root_dir}/#{new_name}"
|
92
|
+
## note: make sure subpath exists (e.g. replacement might include new (sub)dirs too)
|
93
|
+
FileUtils.mkdir_p( File.dirname( dest_path ), flags ) unless Dir.exist?( File.dirname( dest_path ))
|
94
|
+
FileUtils.mv( src_path, dest_path, flags )
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
def merge_files( root_dir, hash, opts={} )
|
103
|
+
## note: rescan files (after renames)
|
104
|
+
tree = find_files( root_dir )
|
105
|
+
pp tree
|
106
|
+
|
107
|
+
puts "walk tree:"
|
108
|
+
merge_files_worker( root_dir, '', hash, tree, opts )
|
109
|
+
end
|
110
|
+
|
111
|
+
def merge_files_worker( root_dir, relative_dir, hash, tree, opts )
|
112
|
+
tree.each do |node|
|
113
|
+
if node.is_a? Array ## assume it's a directory
|
114
|
+
if relative_dir.empty? # e.g. just add w/o leading slash e.g. 'lib' and not '/lib'
|
115
|
+
new_relative_dir = node[0].dup # note: create a new string; just in case
|
116
|
+
else
|
117
|
+
new_relative_dir = "#{relative_dir}/#{node[0]}"
|
118
|
+
end
|
119
|
+
merge_files_worker( root_dir, new_relative_dir, hash, node[1], opts )
|
120
|
+
else ## assume it's a file
|
121
|
+
if relative_dir.empty?
|
122
|
+
relative_path = node
|
123
|
+
else
|
124
|
+
relative_path = "#{relative_dir}/#{node}"
|
125
|
+
end
|
126
|
+
|
127
|
+
src_path = "#{root_dir}/#{relative_path}"
|
128
|
+
|
129
|
+
## note: for now assume always assume text files/utf8
|
130
|
+
old_text = File.open( src_path, 'r:utf-8' ) { |f| f.read }
|
131
|
+
new_text = merge_text( old_text, hash )
|
132
|
+
|
133
|
+
if opts[:o]
|
134
|
+
dest_root = opts[:o]
|
135
|
+
dest_path = "#{dest_root}/#{relative_path}"
|
136
|
+
## make sure dest_path exists
|
137
|
+
dest_dir = File.dirname( dest_path )
|
138
|
+
FileUtils.mkdir_p( dest_dir ) unless Dir.exist?( dest_dir )
|
139
|
+
else
|
140
|
+
dest_root = root_dir
|
141
|
+
dest_path = "#{dest_root}/#{relative_path}"
|
142
|
+
end
|
143
|
+
|
144
|
+
if old_text == new_text
|
145
|
+
if opts[:o] ## for testing copy file 1:1
|
146
|
+
puts " copy file 1:1 #{node} (#{relative_dir}) in (#{dest_root})"
|
147
|
+
FileUtils.cp( src_path, dest_path, verbose: true )
|
148
|
+
else
|
149
|
+
puts " skip file #{node} (#{relative_dir})"
|
150
|
+
end
|
151
|
+
else
|
152
|
+
puts " *** update file #{node} (#{relative_dir}) in (#{dest_root})"
|
153
|
+
File.open( dest_path, 'w:utf-8' ) do |f|
|
154
|
+
f.write( new_text )
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
def merge_path( path, hash )
|
164
|
+
## e.g. allow
|
165
|
+
## __filename__ or
|
166
|
+
## $filename$ for now
|
167
|
+
## note: allow underline too e.g $file_name$ etc.
|
168
|
+
path.gsub( /(__|\$)([a-z_]+)\1/i ) do |_|
|
169
|
+
key = $2.to_s
|
170
|
+
value = hash[ key ]
|
171
|
+
puts " [path] replacing #{key} w/ >#{value}< in (#{path})"
|
172
|
+
value
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def merge_text( text, hash )
|
177
|
+
## e.g. allow
|
178
|
+
## $filename$ for now only in text
|
179
|
+
## note: must include leading and trailing word boundry (/B)
|
180
|
+
## e.g. hello$test$ will not match only "free-standing $test
|
181
|
+
## or in quote e.g. "$test$"
|
182
|
+
## e.g. no letters or digits allowed before or after $ to match
|
183
|
+
## note: allow underline too e.g. $test_klass$ etc.
|
184
|
+
|
185
|
+
## pp text
|
186
|
+
|
187
|
+
text.gsub( /\B\$([a-z_]+)\$\B/i ) do |_|
|
188
|
+
key = $1.to_s
|
189
|
+
value = hash[ key ]
|
190
|
+
puts " [text] replacing #{key} w/ >#{value}<"
|
191
|
+
value
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
def merge( root_dir, hash, opts={} )
|
198
|
+
puts " merge #{root_dir}, #{hash.inspect}"
|
199
|
+
|
200
|
+
merge_filenames( root_dir, hash, opts )
|
201
|
+
merge_files( root_dir, hash, opts )
|
202
|
+
end
|
203
|
+
|
204
|
+
end # class Merger
|
205
|
+
|
206
|
+
end # module Quik
|
207
|
+
|