sow 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/COPYING +622 -0
  2. data/HISTORY +14 -0
  3. data/MANIFEST +138 -0
  4. data/NOTES +86 -0
  5. data/README +157 -0
  6. data/bin/sow +4 -0
  7. data/lib/sow.rb +12 -0
  8. data/lib/sow/command.rb +201 -0
  9. data/lib/sow/context.rb +29 -0
  10. data/lib/sow/core_ext.rb +47 -0
  11. data/lib/sow/generators/base.rb +321 -0
  12. data/lib/sow/generators/create.rb +111 -0
  13. data/lib/sow/generators/delete.rb +50 -0
  14. data/lib/sow/generators/update.rb +47 -0
  15. data/lib/sow/logger.rb +73 -0
  16. data/lib/sow/manager.rb +101 -0
  17. data/lib/sow/metadata.rb +115 -0
  18. data/lib/sow/plugin.rb +484 -0
  19. data/lib/sow/script.rb +186 -0
  20. data/lib/sow/session.rb +150 -0
  21. data/meta/authors +1 -0
  22. data/meta/contact +1 -0
  23. data/meta/description +1 -0
  24. data/meta/homepage +1 -0
  25. data/meta/loadpath +2 -0
  26. data/meta/package +1 -0
  27. data/meta/project +1 -0
  28. data/meta/repository +1 -0
  29. data/meta/requires +2 -0
  30. data/meta/ruby +2 -0
  31. data/meta/version +1 -0
  32. data/plug/sow/seeds/bin/.meta/created +1 -0
  33. data/plug/sow/seeds/bin/.meta/description +1 -0
  34. data/plug/sow/seeds/bin/.meta/license +1 -0
  35. data/plug/sow/seeds/bin/.meta/package +1 -0
  36. data/plug/sow/seeds/bin/.meta/requires +1 -0
  37. data/plug/sow/seeds/bin/.meta/title +1 -0
  38. data/plug/sow/seeds/bin/.meta/version +1 -0
  39. data/plug/sow/seeds/bin/SCRIPT.rb +22 -0
  40. data/plug/sow/seeds/bin/template/bin/command.rb +12 -0
  41. data/plug/sow/seeds/hoe/SCRIPT.rb +28 -0
  42. data/plug/sow/seeds/hoe/template/History.txt +7 -0
  43. data/plug/sow/seeds/hoe/template/Manifest.txt +8 -0
  44. data/plug/sow/seeds/hoe/template/README.txt +49 -0
  45. data/plug/sow/seeds/hoe/template/Rakefile +13 -0
  46. data/plug/sow/seeds/hoe/template/bin/__name__ +0 -0
  47. data/plug/sow/seeds/hoe/template/lib/__name__.rb +4 -0
  48. data/plug/sow/seeds/hoe/template/test/test___name__.rb +1 -0
  49. data/plug/sow/seeds/license/SCRIPT.rb +27 -0
  50. data/plug/sow/seeds/license/template/META/license +1 -0
  51. data/plug/sow/seeds/license/template/gpl/LICENSE +622 -0
  52. data/plug/sow/seeds/license/template/lgpl/LICENSE +789 -0
  53. data/plug/sow/seeds/license/template/mit/LICENSE +22 -0
  54. data/plug/sow/seeds/ruby/COPY.yml +14 -0
  55. data/plug/sow/seeds/ruby/DATA.yml +8 -0
  56. data/plug/sow/seeds/ruby/USAGE.txt +8 -0
  57. data/plug/sow/seeds/ruby/script.sow.rb +17 -0
  58. data/plug/sow/seeds/ruby/template/COPYING +622 -0
  59. data/plug/sow/seeds/ruby/template/History.rdoc +18 -0
  60. data/plug/sow/seeds/ruby/template/README.rdoc +43 -0
  61. data/plug/sow/seeds/ruby/template/README.rdoc.till +43 -0
  62. data/plug/sow/seeds/ruby/template/Rakefile +1 -0
  63. data/plug/sow/seeds/ruby/template/bin/__package__ +2 -0
  64. data/plug/sow/seeds/ruby/template/lib/__package__.rb +3 -0
  65. data/plug/sow/seeds/ruby/template/meta/created +1 -0
  66. data/plug/sow/seeds/ruby/template/meta/description +1 -0
  67. data/plug/sow/seeds/ruby/template/meta/license +1 -0
  68. data/plug/sow/seeds/ruby/template/meta/package +1 -0
  69. data/plug/sow/seeds/ruby/template/meta/requires +1 -0
  70. data/plug/sow/seeds/ruby/template/meta/title +1 -0
  71. data/plug/sow/seeds/ruby/template/meta/version +1 -0
  72. data/plug/sow/seeds/ruby/template/setup.rb +1467 -0
  73. data/plug/sow/seeds/ruby/template/test/template.rb +17 -0
  74. data/plug/sow/seeds/testunit/COPY.yml +12 -0
  75. data/plug/sow/seeds/testunit/DATA.yml +23 -0
  76. data/plug/sow/seeds/testunit/USAGE.txt +11 -0
  77. data/plug/sow/seeds/testunit/_SCRIPT.rb +42 -0
  78. data/plug/sow/seeds/testunit/template/form/testunit +24 -0
  79. data/plug/sow/seeds/testunit/template/test/test_template.rb +15 -0
  80. data/plug/sow/seeds/website/template/assets/styles/color.css +0 -0
  81. data/plug/sow/seeds/website/template/assets/styles/font.css +0 -0
  82. data/plug/sow/seeds/website/template/assets/styles/index.css +4 -0
  83. data/plug/sow/seeds/website/template/assets/styles/reset.css +0 -0
  84. data/plug/sow/seeds/website/template/assets/styles/struct.css +0 -0
  85. data/plug/sow/seeds/website/template/index.html +15 -0
  86. data/test/features/scaffold.feature +13 -0
  87. data/test/features/step_definitions/cli_steps.rb +0 -0
  88. data/test/features/step_definitions/fixture_steps.rb +72 -0
  89. data/test/features/support/env.rb +41 -0
  90. data/test/unit/fixtures/README +5 -0
  91. data/test/unit/helper.rb +23 -0
  92. data/test/unit/test_metadata.rb +17 -0
  93. data/test/unit/test_scaffold.rb +37 -0
  94. metadata +178 -0
@@ -0,0 +1,29 @@
1
+ module Sow
2
+
3
+ FIX = "FIXME"
4
+
5
+ # Erb templates are all rendered within the scope
6
+ # a context object. This limits access to only
7
+ # the those things that are pertinant. All metadata
8
+ # can be accessded by name, as this this object
9
+ # delegate missing methods to a Metadata instance.
10
+ #
11
+ class Context
12
+ instance_methods.each{ |m| private m unless m.to_s =~ /^__/ }
13
+
14
+ def initialize(metadata)
15
+ @metadata = metadata
16
+ end
17
+
18
+ def method_missing(s)
19
+ @metadata.__send__(s) || "#{FIX}: (#{s})"
20
+ end
21
+
22
+ # Processes file through erb.
23
+ def erb(file)
24
+ erb = ERB.new(File.read(file))
25
+ erb.result(binding)
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,47 @@
1
+ require 'erb'
2
+ require 'fileutils'
3
+
4
+ # Facets Core
5
+ require 'facets/kernel/ask'
6
+ require 'facets/hash/rekey'
7
+ require 'facets/dir/multiglob'
8
+ require 'facets/file/rootname'
9
+ require 'facets/file/split_root'
10
+ require 'facets/module/basename'
11
+ require 'facets/string/tab'
12
+ require 'facets/string/pathize'
13
+ require 'facets/string/modulize'
14
+ require 'facets/string/methodize'
15
+
16
+ # Facets More
17
+ require 'facets/ostruct'
18
+ require 'facets/pathname'
19
+
20
+ # ARE THESE NEEDED?
21
+ #require 'facets/yaml' # for to_yamlfrag
22
+ #require 'facets/string/snakecase'
23
+ #require 'facets/string/camelcase'
24
+ #require 'sow/openext'
25
+
26
+ class String
27
+
28
+ def to_list
29
+ split(/[:;\n]/).collect{ |e| e.strip }
30
+ end
31
+
32
+ end
33
+
34
+ # NOTE: These are now in Facets.
35
+
36
+ =begin
37
+ module File
38
+
39
+ #
40
+ def split_root(path)
41
+ path_re = Regexp.new('[' + Regexp.escape(File::Separator + %q{\/}) + ']')
42
+ path.split(path_re, 2)
43
+ end
44
+
45
+ end
46
+ =end
47
+
@@ -0,0 +1,321 @@
1
+ require 'pathname'
2
+ require 'sow/core_ext'
3
+ require 'sow/plugin'
4
+ require 'sow/context'
5
+
6
+ module Sow
7
+
8
+ module Generators
9
+
10
+ # Base Class for the generators. This provides the
11
+ # backbone of Sow's operations. Essentially a plugin
12
+ # provides a copylist to the generator, which cleans
13
+ # it up and uses it for it's specific task,
14
+ # eg. create, update or delete.
15
+ #
16
+ class Base
17
+
18
+ attr :session
19
+ #attr :location
20
+ attr :copylist
21
+
22
+ attr :context
23
+ attr :logger
24
+
25
+ def initialize(session, copylist)
26
+ if session.debug?
27
+ puts "\n[copylist]"
28
+ copylist.each do |loc, tname, fname, opts|
29
+ puts "#{loc} #{tname} #{fname} #{opts.inspect}"
30
+ end
31
+ end
32
+
33
+ @session = session
34
+ #@location = location
35
+ @copylist = copylist
36
+
37
+ @logger = Logger.new(self)
38
+ @context = Context.new(metadata)
39
+ end
40
+
41
+ #
42
+ def metadata
43
+ session.metadata
44
+ end
45
+
46
+ # Where to find plugin files.
47
+ #alias_method :source, :location
48
+
49
+ # TODO: rename to 'destination'
50
+ def output
51
+ @output ||= (
52
+ #if session.create? && plugin.name #scaffold && session.scaffold?
53
+ # session.output + plugin.name
54
+ #else
55
+ session.destination
56
+ #end
57
+ )
58
+ end
59
+
60
+ #
61
+ def arguments
62
+ session.arguments
63
+ end
64
+
65
+ def trial? ; session.trial? ; end
66
+ def debug? ; session.debug? ; end
67
+ def quiet? ; session.quiet? ; end
68
+ def force? ; session.force? ; end
69
+ def prompt? ; session.prompt? ; end
70
+ def skip? ; session.skip? ; end
71
+
72
+ # Newuse of sow? In other words, is the destination empty?
73
+ def newproject?
74
+ session.new?
75
+ end
76
+
77
+ # Main command called to generate files.
78
+ #
79
+ def generate #(args, opts)
80
+ actionlist = actionlist(copylist)
81
+
82
+ if actionlist.empty?
83
+ logger.report_nothing_to_generate
84
+ return
85
+ end
86
+
87
+ source = '' # FIXME
88
+ logger.report_startup(source, output)
89
+ mkdir_p(output) #unless File.directory?(output)
90
+ Dir.chdir(output) do
91
+ actionlist.each do |action, loc, src, dest, opts|
92
+ atime = Time.now
93
+ result, fulldest = *__send__(action, loc, src, dest, opts)
94
+ logger.report_create(relative_to_output(dest), result, atime)
95
+ #logger.report_create(dest, result, atime)
96
+ end
97
+ end
98
+ logger.report_complete
99
+ logger.report_fixes #if session.newproject?
100
+ end
101
+
102
+ private
103
+
104
+ # Processes with erb.
105
+ def erb(file)
106
+ context.erb(file)
107
+ #metadata.erb(file)
108
+ end
109
+
110
+ #
111
+ def mark; 'copy'; end
112
+
113
+ def actionlist(list)
114
+ list = actionlist_sort(list)
115
+ list = actionlist_mark(list)
116
+ list = actionlist_safe(list)
117
+ list = actionlist_check(list)
118
+ list
119
+ end
120
+
121
+ def actionlist_sort(list)
122
+ list
123
+ end
124
+
125
+ # Add copy action.
126
+ def actionlist_mark(list)
127
+ list.map do |args|
128
+ [mark, *args]
129
+ end
130
+ end
131
+
132
+ # If in prompt mode, returns a list filtered of overwrites
133
+ # as selected by the user. If in skip mode, mark duplicates to
134
+ # skipped. If not in prompt or skip mode, simply return the
135
+ # current list.
136
+ #
137
+ def actionlist_safe(list)
138
+ return list unless (prompt? or skip?)
139
+ return list if list.empty?
140
+ safe = []
141
+ dups = []
142
+ list.each do |action, loc, tname, fname, opts|
143
+ dups << [action, loc, tname, fname, opts, (output + fname).file?]
144
+ end
145
+ puts "Select (y/n) which files to overwrite:\n" if prompt? unless quiet?
146
+ dups.each do |action, loc, tname, fname, opts, check|
147
+ if check
148
+ if skip?
149
+ safe << [:skip, loc, tname, fname, opts]
150
+ else
151
+ f = relative_to_output(fname)
152
+ case ans = ask(" #{f}? ").downcase.strip
153
+ when 'y', 'yes'
154
+ safe << [action, loc, tname, fname, opts]
155
+ else
156
+ safe << [:skip, loc, tname, fname, opts]
157
+ end
158
+ end
159
+ else
160
+ safe << [action, loc, tname, fname, opts]
161
+ end
162
+ end
163
+ puts
164
+ return safe
165
+ end
166
+
167
+ def actionlist_check(list)
168
+ check_conflicts(list) # TODO: should this come before or after prompt?
169
+ check_overwrite(list)
170
+ list
171
+ end
172
+
173
+ # Check for any overwrites. If generator allows overwrites
174
+ # this will be skipped, otherewise an error will be raised.
175
+ #
176
+ # TODO: Make this a list filter with check for "identical" files?
177
+ def check_overwrite(list)
178
+ return if force?
179
+ return if prompt?
180
+ return if skip?
181
+ #return if session.overwrite? # TODO: not so sure overwirte? option is a good idea.
182
+
183
+ if newproject? && !output.glob('**/*').empty? # FIXME?
184
+ abort "New project isn't empty. Use --force, --skip or --prompt."
185
+ end
186
+
187
+ clobbers = []
188
+ list.each do |action, loc, tname, fname, opts|
189
+ tpath = loc + tname
190
+ fpath = output + fname
191
+ if fpath.file? #fpath.exist?
192
+ clobbers << relative_to_output(fname)
193
+ end
194
+ end
195
+
196
+ if !clobbers.empty?
197
+ puts " " + clobbers.join("\n ")
198
+ abort "These files would be overwritten. Use --force, --skip or --prompt." # TODO: implement --skip
199
+ end
200
+ end
201
+
202
+ # Check for any clashing generations, ie. a directory that
203
+ # will overwrite a file or a file that will overwrite a
204
+ # directory. This will raise an error if any of these
205
+ # conditions are found, unless force? is set to true.
206
+ #
207
+ def check_conflicts(list)
208
+ #return if force?
209
+ list.each do |action, loc, tname, fname, opts|
210
+ tpath = loc + tname
211
+ fpath = output + fname
212
+ if File.exist?(fpath)
213
+ if tpath.directory?
214
+ if !fpath.directory?
215
+ raise "Directory to be created clashes with a pre-existent file -- #{fname}"
216
+ end
217
+ else
218
+ if fpath.directory?
219
+ raise "File to be created clashes with a pre-existent directory -- #{fname}"
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ #
227
+ def skip(loc, src, dest, opts)
228
+ return 'skip', dest
229
+ end
230
+
231
+ # Access to FileUtils
232
+ def fu
233
+ trial? ? FileUtils::DryRun : FileUtils
234
+ end
235
+
236
+ #
237
+ def cp(src, dest)
238
+ if trial?
239
+ s = src #relative_to_source(src)
240
+ d = relative_to_output(dest)
241
+ puts "cp #{s} #{d}"
242
+ else
243
+ fu.cp(src, dest)
244
+ end
245
+ end
246
+
247
+ #
248
+ def mkdir_p(dir)
249
+ if trial?
250
+ d = relative_to_output(dir)
251
+ puts "mkdir_p #{d}"
252
+ else
253
+ fu.mkdir_p(dir)
254
+ end
255
+ end
256
+
257
+ #
258
+ def chmod(mode, file)
259
+ if trial?
260
+ f = relative_to_output(file)
261
+ puts "chmod #{f} #{mode}"
262
+ else
263
+ fu.chmod(mode, file)
264
+ end
265
+ end
266
+
267
+ # Write file.
268
+ def write(file, text)
269
+ if trial?
270
+ f = relative_to_output(file)
271
+ puts "write #{f}"
272
+ else
273
+ mkdir_p(File.dirname(file))
274
+ File.open(file, 'w'){ |f| f << text }
275
+ end
276
+ end
277
+
278
+ #
279
+ def rm(dest)
280
+ if File.directory?(dest)
281
+ if trial?
282
+ d = relative_to_output(dest)
283
+ puts "rmdir #{d}"
284
+ else
285
+ fu.rmdir(dest)
286
+ end
287
+ else
288
+ if trial?
289
+ d = relative_to_output(dest)
290
+ puts "rm #{d}"
291
+ else
292
+ fu.rm(dest)
293
+ end
294
+ end
295
+ end
296
+
297
+ # FIXME
298
+ #def relative_to_source(src)
299
+ # Pathname.new(src).relative_path_from(source)
300
+ #end
301
+
302
+ #
303
+ def relative_to_output(dest)
304
+ if dest =~ /^\//
305
+ Pathname.new(dest).relative_path_from(output)
306
+ else
307
+ dest
308
+ end
309
+ end
310
+
311
+ #
312
+ def inspect
313
+ "#<#{self.class} @session=#{@session.inspect}>"
314
+ end
315
+
316
+ end
317
+
318
+ end
319
+
320
+ end#module Sow
321
+
@@ -0,0 +1,111 @@
1
+ require 'sow/generators/base'
2
+
3
+ module Sow
4
+
5
+ module Generators
6
+
7
+ # = Create Generator
8
+ #
9
+ class Create < Base
10
+
11
+ #
12
+ def copy(loc, src, dest, opts)
13
+ tmp = File.join(loc, src)
14
+ if File.directory?(tmp)
15
+ copy_dir(loc, src, dest, opts)
16
+ else
17
+ copy_doc(loc, src, dest, opts)
18
+ end
19
+ end
20
+
21
+ #
22
+ def copy_dir(loc, src, dest, opts)
23
+ if File.exist?(dest) #&& src == dest
24
+ how = 'same'
25
+ else
26
+ mkdir_p(dest)
27
+ how = 'create'
28
+ end
29
+ return how, dest
30
+ end
31
+
32
+ # TODO: Can .stub be used to prevent erb?
33
+ def copy_doc(loc, src, dest, opts)
34
+ tmp = File.join(loc, src)
35
+ #ext = File.extname(src)
36
+ #case ext
37
+ #when '.erb'
38
+ if opts['verbatim']
39
+ how = (File.exist?(dest) ? 'update' : 'create')
40
+ #file = tmp_file.chomp('.stub')
41
+ cp(tmp, dest)
42
+ else
43
+ #file = tname.chomp('.erb') # old way
44
+ text = erb(tmp)
45
+ how = (File.exist?(dest) ? 'update' : 'create')
46
+ write(dest, text)
47
+ end
48
+ if opts['chmod']
49
+ chmod(opts['chmod'], dest)
50
+ end
51
+ return how, dest
52
+ end
53
+
54
+ #
55
+ #def erb(file)
56
+ # plugin.erb(file)
57
+ #end
58
+
59
+ =begin
60
+ ###
61
+ def erb(file)
62
+ text = nil
63
+ temp = Context.new(plugin)
64
+ begin
65
+ text = temp.erb(file)
66
+ rescue => e
67
+ if trace?
68
+ raise e
69
+ else
70
+ abort "template error -- #{file}"
71
+ end
72
+ end
73
+ return text
74
+ end
75
+
76
+
77
+ ### Copy a directory varbatim; wich means
78
+ ### just doing a mkdir.
79
+ def verbatim_dir(tname, fname)
80
+ if File.exist?(fname)
81
+ #logger.report_create(fname, 'identical')
82
+ 'identical'
83
+ else
84
+ #logger.report_create(fname, 'create')
85
+ mkdir_p(fname)
86
+ 'create'
87
+ end
88
+ end
89
+
90
+ ### Copy a file verbatim.
91
+ def verbatim_file(tname, fname)
92
+ #ext = File.extname(tname)
93
+ doc = File.join(source, tname)
94
+ if File.exist?(fname)
95
+ how = 'update'
96
+ else
97
+ how = 'create'
98
+ end
99
+ #file = tmp_file.chomp('.stub')
100
+ #file = file.sub('__name__', name)
101
+ cp(doc, fname)
102
+ return how
103
+ end
104
+ =end
105
+
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+