linecook 0.6.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/History +139 -0
  2. data/HowTo/Control Virtual Machines +106 -0
  3. data/HowTo/Generate Scripts +263 -0
  4. data/HowTo/Run Scripts +87 -0
  5. data/HowTo/Setup Virtual Machines +76 -0
  6. data/License.txt +1 -1
  7. data/README +78 -59
  8. data/bin/linecook +12 -5
  9. data/bin/linecook_run +45 -0
  10. data/bin/linecook_scp +50 -0
  11. data/lib/linecook.rb +1 -3
  12. data/lib/linecook/attributes.rb +49 -12
  13. data/lib/linecook/commands.rb +9 -4
  14. data/lib/linecook/commands/build.rb +69 -0
  15. data/lib/linecook/commands/command.rb +13 -3
  16. data/lib/linecook/commands/command_error.rb +6 -0
  17. data/lib/linecook/commands/env.rb +74 -8
  18. data/lib/linecook/commands/helper.rb +271 -24
  19. data/lib/linecook/commands/init.rb +10 -6
  20. data/lib/linecook/commands/package.rb +36 -18
  21. data/lib/linecook/commands/run.rb +66 -0
  22. data/lib/linecook/commands/snapshot.rb +114 -0
  23. data/lib/linecook/commands/ssh.rb +39 -0
  24. data/lib/linecook/commands/start.rb +34 -0
  25. data/lib/linecook/commands/state.rb +32 -0
  26. data/lib/linecook/commands/stop.rb +22 -0
  27. data/lib/linecook/commands/vbox_command.rb +130 -0
  28. data/lib/linecook/cookbook.rb +112 -55
  29. data/lib/linecook/package.rb +293 -109
  30. data/lib/linecook/proxy.rb +19 -0
  31. data/lib/linecook/recipe.rb +321 -62
  32. data/lib/linecook/template.rb +7 -101
  33. data/lib/linecook/test.rb +196 -141
  34. data/lib/linecook/test/command_parser.rb +75 -0
  35. data/lib/linecook/test/file_test.rb +153 -35
  36. data/lib/linecook/test/shell_test.rb +176 -0
  37. data/lib/linecook/utils.rb +25 -7
  38. data/lib/linecook/version.rb +4 -4
  39. data/templates/Rakefile +44 -47
  40. data/templates/_gitignore +1 -1
  41. data/templates/attributes/project_name.rb +4 -4
  42. data/templates/config/ssh +15 -0
  43. data/templates/files/help.txt +1 -0
  44. data/templates/helpers/project_name/assert_content_equal.erb +15 -0
  45. data/templates/helpers/project_name/create_dir.erb +9 -0
  46. data/templates/helpers/project_name/create_file.erb +8 -0
  47. data/templates/helpers/project_name/install_file.erb +8 -0
  48. data/templates/packages/abox.yml +4 -0
  49. data/templates/recipes/abox.rb +22 -0
  50. data/templates/recipes/abox_test.rb +14 -0
  51. data/templates/templates/todo.txt.erb +3 -0
  52. data/templates/test/project_name_test.rb +19 -0
  53. data/templates/test/test_helper.rb +14 -0
  54. metadata +43 -41
  55. data/cookbook +0 -0
  56. data/lib/linecook/commands/helpers.rb +0 -28
  57. data/lib/linecook/commands/vbox.rb +0 -85
  58. data/lib/linecook/helper.rb +0 -117
  59. data/lib/linecook/shell.rb +0 -11
  60. data/lib/linecook/shell/posix.rb +0 -145
  61. data/lib/linecook/shell/test.rb +0 -254
  62. data/lib/linecook/shell/unix.rb +0 -117
  63. data/lib/linecook/shell/utils.rb +0 -138
  64. data/templates/README +0 -90
  65. data/templates/files/file.txt +0 -1
  66. data/templates/helpers/project_name/echo.erb +0 -5
  67. data/templates/recipes/project_name.rb +0 -20
  68. data/templates/scripts/project_name.yml +0 -7
  69. data/templates/templates/template.txt.erb +0 -3
  70. data/templates/vbox/setup/virtual_box +0 -86
  71. data/templates/vbox/ssh/id_rsa +0 -27
  72. data/templates/vbox/ssh/id_rsa.pub +0 -1
data/lib/linecook.rb CHANGED
@@ -1,6 +1,4 @@
1
- require 'linecook/helper'
2
- require 'linecook/script'
3
- require 'linecook/cookbook'
1
+ require 'linecook/package'
4
2
 
5
3
  module Linecook
6
4
  end
@@ -1,22 +1,59 @@
1
- require 'linecook/utils'
2
-
3
1
  module Linecook
2
+
3
+ # Attributes provides a context for specifying default attributes. For
4
+ # example:
5
+ #
6
+ # attributes = Attributes.new
7
+ # attributes.instance_eval %{
8
+ # attrs['a'] = 'A'
9
+ # attrs['b']['c'] = 'C'
10
+ # }
11
+ #
12
+ # attributes.to_hash
13
+ # # => {'a' => 'A', 'b' => {'c' => 'C'}}
14
+ #
15
+ # Note that attrs is an auto-filling nested hash, making it easy to set
16
+ # nested attributes, but it is not indifferent, meaning you do need to
17
+ # differentiate between symbols and strings. Normally strings are
18
+ # preferred.
4
19
  class Attributes
5
- attr_reader :attrs
6
- attr_reader :context
20
+ # A proc used to create nest_hash hashes
21
+ NEST_HASH_PROC = Proc.new do |hash, key|
22
+ hash[key] = Hash.new(&NEST_HASH_PROC)
23
+ end
7
24
 
8
- def initialize(context={})
9
- @context = context
10
- reset(true)
25
+ class << self
26
+ # Returns an auto-filling nested hash.
27
+ def nest_hash
28
+ Hash.new(&NEST_HASH_PROC)
29
+ end
30
+
31
+ # Recursively disables automatic nesting of nest_hash hashes.
32
+ def disable_nest_hash(hash)
33
+ if hash.default_proc == NEST_HASH_PROC
34
+ hash.default = nil
35
+ end
36
+
37
+ hash.each_pair do |key, value|
38
+ if value.kind_of?(Hash)
39
+ disable_nest_hash(value)
40
+ end
41
+ end
42
+
43
+ hash
44
+ end
11
45
  end
12
46
 
13
- def current
14
- @current ||= Utils.serial_merge(attrs, context)
47
+ # An auto-filling nested hash
48
+ attr_reader :attrs
49
+
50
+ def initialize
51
+ @attrs = Attributes.nest_hash
15
52
  end
16
53
 
17
- def reset(full=true)
18
- @attrs = Utils.nest_hash if full
19
- @current = nil
54
+ # Disables automatic nesting and returns attrs.
55
+ def to_hash
56
+ Attributes.disable_nest_hash(attrs)
20
57
  end
21
58
  end
22
59
  end
@@ -1,6 +1,11 @@
1
- require 'linecook/commands/init'
1
+ require 'linecook/commands/build'
2
+ require 'linecook/commands/env'
2
3
  require 'linecook/commands/helper'
3
- require 'linecook/commands/helpers'
4
+ require 'linecook/commands/init'
4
5
  require 'linecook/commands/package'
5
- require 'linecook/commands/env'
6
- require 'linecook/commands/vbox'
6
+ require 'linecook/commands/snapshot'
7
+ require 'linecook/commands/ssh'
8
+ require 'linecook/commands/start'
9
+ require 'linecook/commands/state'
10
+ require 'linecook/commands/stop'
11
+ require 'linecook/commands/run'
@@ -0,0 +1,69 @@
1
+ require 'linecook/commands/helper'
2
+ require 'linecook/commands/package'
3
+
4
+ module Linecook
5
+ module Commands
6
+
7
+ # :startdoc::desc build a project
8
+ #
9
+ # Builds some or all packages and helpers in a project, as needed.
10
+ #
11
+ class Build < Command
12
+ config :project_dir, '.', :short => :d # the project directory
13
+ config :force, false, :short => :f, &c.flag # force creation
14
+ config :quiet, false, &c.flag # silence output
15
+
16
+ def glob_helpers(project_dir)
17
+ helpers_dir = File.expand_path('helpers', project_dir)
18
+ sources = {}
19
+ helpers = []
20
+
21
+ Dir.glob("#{helpers_dir}/*/**/*").each do |source|
22
+ next if File.directory?(source)
23
+ (sources[File.dirname(source)] ||= []) << source
24
+ end
25
+
26
+ sources.each_pair do |dir, sources|
27
+ name = dir[(helpers_dir.length + 1)..-1]
28
+ helpers << [name, sources]
29
+ end
30
+
31
+ helpers.sort_by {|name, sources| name }
32
+ end
33
+
34
+ def glob_package_files(package_names)
35
+ if package_names.empty?
36
+ pattern = File.expand_path('packages/*.yml', project_dir)
37
+ Dir.glob(pattern).select {|path| File.file?(path) }
38
+ else
39
+ package_names.collect do |package_name|
40
+ File.expand_path("packages/#{package_name}.yml", project_dir)
41
+ end
42
+ end
43
+ end
44
+
45
+ def process(*package_names)
46
+ helper = Helper.new(
47
+ :project_dir => project_dir,
48
+ :force => force,
49
+ :quiet => true
50
+ )
51
+
52
+ helpers = glob_helpers(project_dir)
53
+ helpers.each do |(name, sources)|
54
+ helper.process(name, *sources)
55
+ end
56
+
57
+ package = Package.new(
58
+ :project_dir => project_dir,
59
+ :force => force,
60
+ :quiet => quiet
61
+ )
62
+
63
+ glob_package_files(package_names).collect do |package_file|
64
+ package.process(package_file)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -24,16 +24,26 @@ module Linecook
24
24
  lazy_attr :args, :process
25
25
  lazy_register :process, Lazydoc::Arguments
26
26
 
27
- def initialize(config)
27
+ attr_accessor :quiet
28
+
29
+ def initialize(config={})
30
+ @quiet = true
28
31
  initialize_config(config)
29
32
  end
30
33
 
31
34
  def log(action, msg)
32
- puts(" %s %s" % [action, msg])
35
+ $stderr.puts(" %s %s" % [action, msg])
33
36
  end
34
37
 
35
38
  def sh(cmd)
36
- system cmd
39
+ puts "% #{cmd}" unless quiet
40
+ system(cmd)
41
+ end
42
+
43
+ def sh!(cmd)
44
+ unless sh(cmd)
45
+ raise CommandError.new("", $?.exitstatus)
46
+ end
37
47
  end
38
48
 
39
49
  def call(argv)
@@ -1,6 +1,12 @@
1
1
  module Linecook
2
2
  module Commands
3
3
  class CommandError < RuntimeError
4
+ attr_reader :exitstatus
5
+
6
+ def initialize(msg, exitstatus=1)
7
+ @exitstatus = exitstatus
8
+ super(msg)
9
+ end
4
10
  end
5
11
  end
6
12
  end
@@ -1,22 +1,88 @@
1
1
  require 'linecook/commands/command'
2
2
  require 'linecook/cookbook'
3
+ require 'yaml'
3
4
 
4
5
  module Linecook
5
6
  module Commands
6
7
 
7
- # ::desc prints the cookbook env
8
+ # :startdoc::desc prints a package env
8
9
  #
9
- # Print the cookbook env.
10
+ # Prints the env for the current project directory. Specifically the
11
+ # cookbook file is loaded and used to determine all resources that are
12
+ # current available. The full build env for a package can be viewed by
13
+ # specifying the package file as an option.
10
14
  #
15
+ # A specific env value can be printed by specifying the key path to it.
11
16
  class Env < Command
12
- config :cookbook_dir, '.', :short => :d # the cookbook directory
13
- config :path, nil # package path
17
+ config :project_dir, '.', :short => :d # the project directory
18
+ config :package_file, nil, :short => :p # the package file
14
19
 
15
- def process(*keys)
16
- current = Linecook::Cookbook.init(cookbook_dir).env(path)
17
- keys.each {|key| current = current[key] if current }
20
+ # :stopdoc:
21
+ # Evaluate to replace the to_yaml function on Hash so that it will
22
+ # serialize keys in order. Evaluate the OFF code to turn this hack off
23
+ # (and thereby ease up on the code pollution)
24
+ #
25
+ # Modified from: http://snippets.dzone.com/posts/show/5811 Original
26
+ # func: /usr/lib/ruby/1.8/yaml/rubytypes.rb
27
+ ORIGINAL_TO_YAML = 'linecook_original_to_yaml'
28
+ SORTED_HASH_ON_LINE = __LINE__ + 1
29
+ SORTED_HASH_ON = %{
30
+ class Hash
31
+ unless instance_methods.include?('#{ORIGINAL_TO_YAML}')
32
+ alias #{ORIGINAL_TO_YAML} to_yaml
33
+ undef_method :to_yaml
34
+ def to_yaml( opts = {} )
35
+ YAML::quick_emit( object_id, opts ) do |out|
36
+ out.map( taguri, to_yaml_style ) do |map|
37
+ keys.sort_by do |k|
38
+ k.to_s
39
+ end.each do |k|
40
+ map.add( k, fetch(k) )
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end}
47
+
48
+ SORTED_HASH_OFF_LINE = __LINE__ + 1
49
+ SORTED_HASH_OFF = %{
50
+ class Hash
51
+ if instance_methods.include?('#{ORIGINAL_TO_YAML}')
52
+ undef_method :to_yaml
53
+ alias to_yaml #{ORIGINAL_TO_YAML}
54
+ undef_method :#{ORIGINAL_TO_YAML}
55
+ end
56
+ end}
57
+ # :startdoc:
58
+
59
+ def select(current, *keys)
60
+ keys.each do |key|
61
+ unless current.kind_of?(Hash)
62
+ return nil
63
+ end
64
+
65
+ current = current[key]
66
+ end
18
67
 
19
- YAML.dump(current, $stdout)
68
+ current
69
+ end
70
+
71
+ # Serializes the env to the target as YAML. Ensures hashes are
72
+ # serialized with their keys sorted by their to_s value.
73
+ def serialize(env, target="")
74
+ begin
75
+ eval SORTED_HASH_ON, TOPLEVEL_BINDING, __FILE__, SORTED_HASH_ON_LINE
76
+ YAML.dump(env, target)
77
+ ensure
78
+ eval SORTED_HASH_OFF, TOPLEVEL_BINDING, __FILE__, SORTED_HASH_OFF_LINE
79
+ end
80
+ end
81
+
82
+ def process(*keys)
83
+ package = Linecook::Package.init(package_file, project_dir)
84
+ env = select(package.env, *keys)
85
+ serialize(env, $stdout)
20
86
  end
21
87
  end
22
88
  end
@@ -1,51 +1,298 @@
1
1
  require 'linecook/commands/command'
2
- require 'linecook/helper'
3
2
  require 'linecook/utils'
3
+ require 'fileutils'
4
+ require 'erb'
4
5
 
5
6
  module Linecook
6
7
  module Commands
7
8
 
8
- # ::desc generates a helper
9
+ # :startdoc::desc generates a helper module
10
+ #
11
+ # Generates the specified helper module from a set of source files. Each
12
+ # source file becomes a method in the module, named after the source file
13
+ # itself.
14
+ #
15
+ # The helper module will be generated under the lib directory in a file
16
+ # corresponding to const_name (which can also be a constant path). By
17
+ # default, all files under the corresponding helpers directory will be
18
+ # used as sources. For example these are equivalent and produce the
19
+ # Const::Name module in 'lib/const/name.rb':
20
+ #
21
+ # % linecook helper Const::Name
22
+ # % linecook helper const/name
23
+ # % linecook helper const/name helpers/const/name/*
24
+ #
25
+ # == Source Files
26
+ #
27
+ # The contents of the source file are translated into code according to
28
+ # the source file extname.
29
+ #
30
+ # extname translation
31
+ # .rb file defines method body
32
+ # .erb file defines an ERB template (compiled to ruby code)
33
+ #
34
+ # Source files can specify documenation and a method signature using a
35
+ # standard header separated from the body by a double-dash. For example
36
+ # this:
37
+ #
38
+ # [echo.erb]
39
+ # Echo arguments out to the target.
40
+ # (*args)
41
+ # --
42
+ # echo <%= args.join(' ') %>
43
+ #
44
+ # Is translated into something like:
45
+ #
46
+ # # Echo arguments out to the target.
47
+ # def echo(*args)
48
+ # eval ERB.new("echo <%= args.join(' ') %>").src
49
+ # end
50
+ #
51
+ # A second method is also generated to return the result without writing
52
+ # it to the target. The latter method is prefixed by and underscore
53
+ # like:
54
+ #
55
+ # # Return the output of echo, without writing to the target
56
+ # def _echo(*args)
57
+ # ...
58
+ # end
59
+ #
60
+ # Check and bang methods can be specified by adding -check and -bang to
61
+ # the end of the file name. These extensions are stripped off like:
62
+ #
63
+ # [file-check.erb] # => def file? ...
64
+ # [make-bang.rb] # => def make! ...
65
+ #
66
+ # Otherwise the basename of the source file must be a word; non-word
67
+ # basenames raise an error.
68
+ #
69
+ # == Section Files
70
+ #
71
+ # Special section files can be used to define non-standard code in the
72
+ # following places:
73
+ #
74
+ # [:header]
75
+ # module Const
76
+ # [:doc]
77
+ # module Name
78
+ # [:head]
79
+ # ...
80
+ # [:foot]
81
+ # end
82
+ # end
83
+ # [:footer]
84
+ #
85
+ # Section files are defined by prepending '-' to the file basename (like
86
+ # path/to/-header.rb) and are not processed like other source files;
87
+ # instead the contents are directly transcribed into the target file.
9
88
  class Helper < Command
10
- config :cookbook_dir, '.', :short => :d # the cookbook directory
11
- config :namespace, 'linebook', :short => :n # the helper namespace
89
+ config :project_dir, '.', :short => :d # the project directory
12
90
  config :force, false, :short => :f, &c.flag # force creation
91
+ config :quiet, false, &c.flag
13
92
 
14
93
  include Utils
15
94
 
16
- def process(name, *sources)
17
- name = underscore(name)
18
-
19
- const_path = namespace ? File.join(namespace, name) : name
95
+ def process(const_name, *sources)
96
+ const_path = underscore(const_name)
20
97
  const_name = camelize(const_path)
21
98
 
22
- sources = default_sources(name) if sources.empty?
23
- target = File.expand_path(File.join('lib', "#{const_path}.rb"), cookbook_dir)
99
+ unless const_name?(const_name)
100
+ raise "invalid constant name: #{const_name.inspect}"
101
+ end
102
+
103
+ sources = default_sources(const_path) if sources.empty?
104
+ target = File.expand_path(File.join('lib', "#{const_path}.rb"), project_dir)
24
105
 
25
106
  if sources.empty?
26
- raise CommandError, "no sources specified (and none could be found)"
107
+ raise CommandError, "no sources specified (and none found under 'helpers/#{const_path}')"
27
108
  end
28
109
 
29
- if File.exists?(target) && !force
30
- raise CommandError, "already exists: #{target}"
110
+ if force || !FileUtils.uptodate?(target, sources)
111
+ content = build(const_name, sources)
112
+
113
+ target_dir = File.dirname(target)
114
+ unless File.exists?(target_dir)
115
+ FileUtils.mkdir_p(target_dir)
116
+ end
117
+
118
+ File.open(target, 'w') {|io| io << content }
119
+ $stdout.puts target unless quiet
31
120
  end
32
121
 
33
- log :create, const_name
122
+ target
123
+ end
124
+
125
+ # Returns the default source files for a given constant path, which are
126
+ # all files under the 'project_dir/helpers/const_path' folder.
127
+ def default_sources(const_path)
128
+ pattern = File.join(project_dir, 'helpers', const_path, '*')
129
+ sources = Dir.glob(pattern)
130
+ sources.select {|path| File.file?(path) }
131
+ end
132
+
133
+ # returns true if const_name is a valid constant name.
134
+ def const_name?(const_name) # :nodoc:
135
+ const_name =~ /\A(?:::)?[A-Z]\w*(?:::[A-Z]\w*)*\z/
136
+ end
137
+
138
+ # helper to partition an array of source files into section and
139
+ # defintion files
140
+ def partition(sources) # :nodoc:
141
+ sources.partition do |path|
142
+ basename = File.basename(path)
143
+ extname = File.extname(path)
144
+ basename[0] == ?- && basename.chomp(extname) != '-'
145
+ end
146
+ end
147
+
148
+ # helper to load each section path into a sections hash; removes the
149
+ # leading - from the path basename to determine the section key.
150
+ def load_sections(paths) # :nodoc:
151
+ sections = {}
34
152
 
35
- helper = Linecook::Helper.new(const_name, sources)
36
- content = helper.build
37
-
38
- target_dir = File.dirname(target)
39
- unless File.exists?(target_dir)
40
- FileUtils.mkdir_p(target_dir)
153
+ paths.each do |path|
154
+ basename = File.basename(path)
155
+ extname = File.extname(path)
156
+ key = basename[1, basename.length - extname.length - 1]
157
+ sections[key] = File.read(path)
41
158
  end
42
-
43
- File.open(target, 'w') {|io| io << content }
159
+
160
+ sections
44
161
  end
45
162
 
46
- def default_sources(name)
47
- Dir.glob File.join(cookbook_dir, 'helpers', name, '**/*')
163
+ # helper to load and parse a definition file
164
+ def load_definition(path) # :nodoc:
165
+ extname = File.extname(path)
166
+ name = File.basename(path).chomp(extname)
167
+ desc, signature, body = parse_definition(File.read(path))
168
+
169
+ [desc, parse_method_name(name), signature, method_body(body, extname)]
170
+ rescue CommandError
171
+ err = CommandError.new("#{$!.message} (#{path.inspect})")
172
+ err.set_backtrace($!.backtrace)
173
+ raise err
48
174
  end
175
+
176
+ # helper to reformat special basenames (in particular -check and -bang)
177
+ # to their corresponding method_name
178
+ def parse_definition(str) # :nodoc:
179
+ head, body = str.split(/^--.*\n/, 2)
180
+ head, body = '', head if body.nil?
181
+
182
+ found_signature = false
183
+ signature, desc = head.split("\n").partition do |line|
184
+ found_signature = true if line =~ /^\s*\(.*?\)/
185
+ found_signature
186
+ end
187
+
188
+ [desc.join("\n"), found_signature ? signature.join("\n") : '()', body.to_s]
189
+ end
190
+
191
+ # helper to reformat special basenames (in particular -check and -bang)
192
+ # to their corresponding method_name
193
+ def parse_method_name(basename) # :nodoc:
194
+ case basename
195
+ when /-check\z/ then basename.sub(/-check$/, '?')
196
+ when /-bang\z/ then basename.sub(/-bang$/, '!')
197
+ when /-eq\z/ then basename.sub(/-eq$/, '=')
198
+ when /\A\w+\z/ then basename
199
+ else raise CommandError.new("invalid method name: #{basename.inspect}")
200
+ end
201
+ end
202
+
203
+ # helper to reformat a definition body according to a given extname. rb
204
+ # content is rstripped to improve formatting. erb content is compiled
205
+ # and the source is placed as a comment before it (to improve
206
+ # debugability).
207
+ def method_body(body, extname) # :nodoc:
208
+ case extname
209
+ when '.erb'
210
+ source = "# #{body.gsub(/\n/, "\n# ")}"
211
+ compiler = ERB::Compiler.new('<>')
212
+ compiler.put_cmd = "write"
213
+ compiler.insert_cmd = "write"
214
+ code = compiler.compile(body)
215
+
216
+ "#{source}\n#{code}".gsub(/^(\s*)/) do |m|
217
+ indent = 2 + $1.length - ($1.length % 2)
218
+ ' ' * indent
219
+ end
220
+
221
+ when '.rb'
222
+ body.rstrip
223
+
224
+ else
225
+ raise CommandError.new("invalid definition format: #{extname.inspect}")
226
+ end
227
+ end
228
+
229
+ # helper to nest a module body within a const_name. documentation
230
+ # can be provided for the innermost constant.
231
+ def module_nest(const_name, body, inner_doc=nil) # :nodoc:
232
+ body = body.strip.split("\n")
233
+
234
+ const_name.split(/::/).reverse_each do |name|
235
+ body.collect! {|line| " #{line}" }
236
+
237
+ body.unshift "module #{name}"
238
+ body.push "end"
239
+
240
+ # prepend the inner doc to the innermost const
241
+ if inner_doc
242
+ body = inner_doc.strip.split("\n") + body
243
+ inner_doc = nil
244
+ end
245
+ end
246
+
247
+ body.join("\n")
248
+ end
249
+
250
+ # Returns the code for a const_name module as defined by the source
251
+ # files.
252
+ def build(const_name, sources)
253
+ section_paths, definition_paths = partition(sources)
254
+ sections = load_sections(section_paths)
255
+ definitions = definition_paths.collect {|path| load_definition(path) }
256
+
257
+ body = eval DEFINITION_TEMPLATE, binding, __FILE__, DEFINITION_TEMPLATE_LINE
258
+ code = eval MODULE_TEMPLATE, binding, __FILE__, MODULE_TEMPLATE_LINE
259
+
260
+ code
261
+ end
262
+
263
+ # :stopdoc:
264
+ MODULE_TEMPLATE_LINE = __LINE__ + 2
265
+ MODULE_TEMPLATE = ERB.new(<<-DOC, nil, '<>').src
266
+ # Generated by Linecook
267
+ <%= sections['header'] %>
268
+
269
+ <%= module_nest(const_name, body, sections['doc']) %>
270
+
271
+ <%= sections['footer'] %>
272
+ DOC
273
+
274
+ DEFINITION_TEMPLATE_LINE = __LINE__ + 2
275
+ DEFINITION_TEMPLATE = ERB.new(<<-DOC, nil, '<>').src
276
+ <%= sections['head'] %>
277
+ <% definitions.each do |desc, method_name, signature, body| %>
278
+ <% desc.split("\n").each do |line| %>
279
+ # <%= line %><% end %>
280
+ def <%= method_name %><%= signature %>
281
+ <%= body %>
282
+
283
+ chain_proxy
284
+ end
285
+
286
+ def _<%= method_name %>(*args, &block) # :nodoc:
287
+ str = capture_str { <%= method_name %>(*args, &block) }
288
+ str.strip!
289
+ str
290
+ end
291
+ <% end %>
292
+
293
+ <%= sections['foot'] %>
294
+ DOC
295
+ # :startdoc:
49
296
  end
50
297
  end
51
298
  end