linecook 0.6.2

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.
Files changed (47) hide show
  1. data/History +60 -0
  2. data/License.txt +22 -0
  3. data/README +98 -0
  4. data/bin/linecook +58 -0
  5. data/cookbook +0 -0
  6. data/lib/linecook/attributes.rb +22 -0
  7. data/lib/linecook/commands/command.rb +48 -0
  8. data/lib/linecook/commands/command_error.rb +6 -0
  9. data/lib/linecook/commands/env.rb +23 -0
  10. data/lib/linecook/commands/helper.rb +51 -0
  11. data/lib/linecook/commands/helpers.rb +28 -0
  12. data/lib/linecook/commands/init.rb +82 -0
  13. data/lib/linecook/commands/package.rb +39 -0
  14. data/lib/linecook/commands/vbox.rb +85 -0
  15. data/lib/linecook/commands.rb +6 -0
  16. data/lib/linecook/cookbook.rb +104 -0
  17. data/lib/linecook/helper.rb +117 -0
  18. data/lib/linecook/package.rb +197 -0
  19. data/lib/linecook/recipe.rb +103 -0
  20. data/lib/linecook/shell/posix.rb +145 -0
  21. data/lib/linecook/shell/test.rb +254 -0
  22. data/lib/linecook/shell/unix.rb +117 -0
  23. data/lib/linecook/shell/utils.rb +138 -0
  24. data/lib/linecook/shell.rb +11 -0
  25. data/lib/linecook/template.rb +111 -0
  26. data/lib/linecook/test/file_test.rb +77 -0
  27. data/lib/linecook/test/regexp_escape.rb +86 -0
  28. data/lib/linecook/test.rb +172 -0
  29. data/lib/linecook/utils.rb +53 -0
  30. data/lib/linecook/version.rb +8 -0
  31. data/lib/linecook.rb +6 -0
  32. data/templates/Gemfile +2 -0
  33. data/templates/README +90 -0
  34. data/templates/Rakefile +149 -0
  35. data/templates/_gitignore +5 -0
  36. data/templates/attributes/project_name.rb +4 -0
  37. data/templates/cookbook +9 -0
  38. data/templates/files/file.txt +1 -0
  39. data/templates/helpers/project_name/echo.erb +5 -0
  40. data/templates/project_name.gemspec +30 -0
  41. data/templates/recipes/project_name.rb +20 -0
  42. data/templates/scripts/project_name.yml +7 -0
  43. data/templates/templates/template.txt.erb +3 -0
  44. data/templates/vbox/setup/virtual_box +86 -0
  45. data/templates/vbox/ssh/id_rsa +27 -0
  46. data/templates/vbox/ssh/id_rsa.pub +1 -0
  47. metadata +166 -0
@@ -0,0 +1,104 @@
1
+ require 'linecook/package'
2
+ require 'yaml'
3
+
4
+ module Linecook
5
+ class Cookbook
6
+ class << self
7
+ def config_file(dir)
8
+ Dir.glob(File.join(dir, '{C,c}ookbook')).first
9
+ end
10
+
11
+ def init(dir)
12
+ path = config_file(dir)
13
+ config = path ? YAML.load_file(path) : nil
14
+
15
+ new(dir, config || {})
16
+ end
17
+
18
+ def gems
19
+ return [] unless Object.const_defined?(:Gem)
20
+
21
+ Gem.source_index.latest_specs.select do |spec|
22
+ config_file(spec.full_gem_path) != nil
23
+ end.collect do |spec|
24
+ spec.name
25
+ end
26
+ end
27
+ end
28
+
29
+ PATTERNS = [
30
+ File.join('attributes', '**', '*.rb'),
31
+ File.join('files', '**', '*'),
32
+ File.join('recipes', '**', '*.rb'),
33
+ File.join('templates', '**', '*.erb')
34
+ ]
35
+
36
+ attr_reader :dir
37
+ attr_reader :config
38
+
39
+ def initialize(dir='.', config={})
40
+ @dir = File.expand_path(dir)
41
+ @config = {
42
+ 'manifest' => {},
43
+ 'paths' => ['.'],
44
+ 'gems' => self.class.gems
45
+ }.merge(config)
46
+ end
47
+
48
+ def manifest
49
+ @manifest ||= begin
50
+ manifest = {}
51
+
52
+ paths = split config['paths']
53
+ gems = split config['gems']
54
+ gems = resolve gems
55
+
56
+ (gems + paths).each do |path|
57
+ path = File.expand_path(path, dir)
58
+ start = path.length + 1
59
+
60
+ PATTERNS.each do |pattern|
61
+ Dir.glob(File.join(path, pattern)).each do |full_path|
62
+ next unless File.file?(full_path)
63
+
64
+ rel_path = full_path[start, full_path.length - start]
65
+ manifest[rel_path] = full_path
66
+ end
67
+ end
68
+ end
69
+
70
+ overrides = config['manifest']
71
+ manifest.merge!(overrides)
72
+ manifest
73
+ end
74
+ end
75
+
76
+ def env(path=nil)
77
+ Package.env(manifest, path)
78
+ end
79
+
80
+ private
81
+
82
+ def split(str) # :nodoc:
83
+ str.kind_of?(String) ? str.split(':') : str
84
+ end
85
+
86
+ def resolve(gems) # :nodoc:
87
+ return gems if gems.empty?
88
+ specs = latest_specs
89
+
90
+ gems.collect do |name|
91
+ spec = specs[name] or raise "no such gem: #{name.inspect}"
92
+ spec.full_gem_path
93
+ end
94
+ end
95
+
96
+ def latest_specs # :nodoc:
97
+ latest = {}
98
+ Gem.source_index.latest_specs.each do |spec|
99
+ latest[spec.name] = spec
100
+ end
101
+ latest
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,117 @@
1
+ require 'linecook/template'
2
+
3
+ module Linecook
4
+ class Helper < Template
5
+ attr_reader :const_name
6
+ attr_reader :sources
7
+
8
+ def initialize(const_name, sources)
9
+ @const_name = const_name
10
+ @sources = sources.select {|source| File.file?(source) }
11
+ @section_paths, @definition_paths = @sources.partition {|path| File.basename(path)[0] == ?_ }
12
+ super()
13
+ end
14
+
15
+ def build
16
+ eval MODULE_TEMPLATE, binding, __FILE__, MODULE_TEMPLATE_LINE
17
+ result
18
+ end
19
+
20
+ def sections
21
+ @sections ||= begin
22
+ sections = {}
23
+
24
+ @section_paths.each do |path|
25
+ key = File.basename(path)[1..-1]
26
+ key.chomp! File.extname(path)
27
+ sections[key.to_sym] = File.read(path)
28
+ end
29
+
30
+ sections
31
+ end
32
+ end
33
+
34
+ def definitions
35
+ @definitions ||= @definition_paths.collect do |path|
36
+ name = File.basename(path).chomp File.extname(path)
37
+ desc, signature, body = parse File.read(path)
38
+
39
+ eval ERB_TEMPLATE, binding, __FILE__, ERB_TEMPLATE_LINE
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def parse(str)
46
+ head, body = str.split(/^--.*\n/, 2)
47
+ head, body = '', head if body.nil?
48
+ signature, desc = parse_head(head)
49
+
50
+ [desc.join("\n"), signature.join("\n"), body]
51
+ end
52
+
53
+ def parse_head(head)
54
+ found_signature = false
55
+ head.split("\n").partition do |line|
56
+ found_signature = true if line =~ /^\s*\(.*?\)/
57
+ found_signature
58
+ end
59
+ end
60
+
61
+ def method_name(name)
62
+ case name
63
+ when /_check$/ then name.sub(/_check$/, '?')
64
+ when /_bang$/ then name.sub(/_bang$/, '!')
65
+ else name
66
+ end
67
+ end
68
+
69
+ def module_nest(const_name, indent=" ", line_sep="\n")
70
+ nestings = const_name.split(/::/).collect {|name| ["module #{name}", "end"]}
71
+ nestings << {:indent => indent, :line_sep => line_sep}
72
+
73
+ nest(*nestings) { yield }
74
+ end
75
+
76
+ MODULE_TEMPLATE_LINE = __LINE__ + 2
77
+ MODULE_TEMPLATE = "self." + ERB.new(<<-DOC, nil, '<>').src
78
+ require 'erb'
79
+ <%= sections[:header] %>
80
+
81
+ # Generated by Linecook, do not edit.
82
+ <% module_nest(const_name, '') do %>
83
+ <%= sections[:head] %>
84
+ <% definitions.each do |definition| %>
85
+
86
+ <%= definition %>
87
+ <% end %>
88
+ <%= sections[:tail] %>
89
+ <% end %>
90
+
91
+ <%= sections[:footer] %>
92
+ DOC
93
+
94
+ ERB_TEMPLATE_LINE = __LINE__ + 2
95
+ ERB_TEMPLATE = ERB.new(<<-DOC, nil, '<>').src
96
+ # :stopdoc:
97
+ <%= name.upcase %>_LINE = __LINE__ + 2
98
+ <%= name.upcase %> = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
99
+ <%= body %>
100
+
101
+ END_OF_TEMPLATE
102
+ # :startdoc:
103
+
104
+ <% desc.each do |line| %>
105
+ # <%= line %>
106
+ <% end %>
107
+ def <%= method_name(name) %><%= signature %>
108
+ eval(<%= name.upcase %>, binding, __FILE__, <%= name.upcase %>_LINE)
109
+ nil
110
+ end
111
+
112
+ def _<%= method_name(name) %>(*args, &block) # :nodoc:
113
+ capture { <%= method_name(name) %>(*args, &block) }
114
+ end
115
+ DOC
116
+ end
117
+ end
@@ -0,0 +1,197 @@
1
+ require 'linecook/utils'
2
+ require 'tempfile'
3
+
4
+ module Linecook
5
+ class Package
6
+ class << self
7
+ def load_env(path)
8
+ (path ? YAML.load_file(path) : nil) || {}
9
+ end
10
+
11
+ def env(manifest, path)
12
+ default = {CONFIG_KEY => {MANIFEST_KEY => manifest}}
13
+ overrides = load_env(path)
14
+ Utils.serial_merge(default, overrides)
15
+ end
16
+
17
+ def init(env={})
18
+ env.kind_of?(Package) ? env : new(env)
19
+ end
20
+ end
21
+
22
+ CONFIG_KEY = 'linecook'
23
+ MANIFEST_KEY = 'manifest'
24
+ REGISTRY_KEY = 'registry'
25
+ CACHE_KEY = 'cache'
26
+ FILES_KEY = 'files'
27
+ TEMPLATES_KEY = 'templates'
28
+ RECIPES_KEY = 'recipes'
29
+ PATHS_KEY = 'paths'
30
+ GEMS_KEY = 'gems'
31
+
32
+ attr_reader :env
33
+
34
+ def initialize(env={})
35
+ @env = env
36
+ end
37
+
38
+ def config
39
+ env[CONFIG_KEY] ||= {}
40
+ end
41
+
42
+ def cache
43
+ config[CACHE_KEY] ||= {}
44
+ end
45
+
46
+ def manifest
47
+ config[MANIFEST_KEY] ||= {}
48
+ end
49
+
50
+ def registry
51
+ config[REGISTRY_KEY] ||= {}
52
+ end
53
+
54
+ def reverse_registry
55
+ cache[:reverse_registry] ||= {}
56
+ end
57
+
58
+ def tempfiles
59
+ cache[:tempfiles] ||= []
60
+ end
61
+
62
+ def register(source_path, build_path=nil)
63
+ source_path = File.expand_path(source_path)
64
+ build_path ||= File.basename(source_path)
65
+
66
+ count = 0
67
+ registry.each_key do |path|
68
+ if path.kind_of?(String) && path.index(build_path) == 0
69
+ count += 1
70
+ end
71
+ end
72
+
73
+ if count > 0
74
+ build_path = "#{build_path}.#{count}"
75
+ end
76
+
77
+ registry[build_path] = source_path
78
+ reverse_registry[source_path.to_sym] = build_path
79
+
80
+ build_path
81
+ end
82
+
83
+ def registered?(source_path)
84
+ source_path = File.expand_path(source_path)
85
+ reverse_registry.has_key?(source_path.to_sym)
86
+ end
87
+
88
+ def built?(build_path)
89
+ registry.has_key?(build_path)
90
+ end
91
+
92
+ def tempfile?(source_path)
93
+ tempfiles.find {|tempfile| tempfile.path == source_path }
94
+ end
95
+
96
+ def build(build_path, source_path=nil)
97
+ case
98
+ when built?(build_path)
99
+ raise "already built: #{build_path}"
100
+
101
+ when source_path
102
+ register(source_path, build_path)
103
+
104
+ else
105
+ tempfile = Tempfile.new File.basename(build_path)
106
+
107
+ register(tempfile.path, build_path)
108
+ tempfiles << tempfile
109
+
110
+ tempfile
111
+ end
112
+ end
113
+
114
+ def build_path(source_path)
115
+ source_path = File.expand_path(source_path)
116
+ reverse_registry[source_path.to_sym]
117
+ end
118
+
119
+ def source_path(build_path)
120
+ registry[build_path]
121
+ end
122
+
123
+ def files
124
+ normalize(FILES_KEY)
125
+ end
126
+
127
+ def templates
128
+ normalize(TEMPLATES_KEY)
129
+ end
130
+
131
+ def recipes
132
+ normalize(RECIPES_KEY)
133
+ end
134
+
135
+ def export(dir, options={})
136
+ close
137
+
138
+ options = {
139
+ :allow_move => true
140
+ }.merge(options)
141
+
142
+ allow_move = options[:allow_move]
143
+
144
+ results = {}
145
+ registry.each_pair do |build_path, source_path|
146
+ target_path = File.join(dir, build_path)
147
+ target_dir = File.dirname(target_path)
148
+
149
+ unless File.exists?(target_dir)
150
+ FileUtils.mkdir_p(target_dir)
151
+ end
152
+
153
+ if allow_move && tempfile?(source_path)
154
+ FileUtils.mv(source_path, target_path)
155
+ else
156
+ FileUtils.cp(source_path, target_path)
157
+ end
158
+
159
+ results[build_path] = target_path
160
+ end
161
+ results
162
+ end
163
+
164
+ def close
165
+ tempfiles.each do |tempfile|
166
+ tempfile.close unless tempfile.closed?
167
+ end
168
+ self
169
+ end
170
+
171
+ private
172
+
173
+ def normalize(key)
174
+ obj = config[key]
175
+
176
+ case obj
177
+ when Hash
178
+ obj
179
+
180
+ when nil
181
+ config[key] = {}
182
+
183
+ when Array
184
+ hash = {}
185
+ obj.each {|entry| hash[entry] = entry }
186
+ config[key] = hash
187
+
188
+ when String
189
+ config[key] = obj.split(':')
190
+ normalize(key)
191
+
192
+ else
193
+ raise "invalid #{key}: #{obj.inspect}"
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,103 @@
1
+ require 'linecook/template'
2
+ require 'linecook/attributes'
3
+ require 'linecook/package'
4
+ require 'linecook/utils'
5
+
6
+ module Linecook
7
+ class Recipe < Template
8
+ class << self
9
+ def build(env)
10
+ package = Package.new(env)
11
+
12
+ package.recipes.each do |recipe_name, target_name|
13
+ new(target_name, env).evaluate(recipe_name)
14
+ end
15
+
16
+ package.close
17
+ package
18
+ end
19
+ end
20
+
21
+ alias target erbout
22
+
23
+ attr_reader :target_name
24
+
25
+ def initialize(target_name, env={})
26
+ @target_name = target_name
27
+ @package = Package.init(env)
28
+ @attributes = Attributes.new(@package.env)
29
+ @erbout = @package.build(target_name)
30
+ end
31
+
32
+ def source_path(*relative_path)
33
+ path = File.join(*relative_path)
34
+ @package.manifest[path] or raise "no such file in manifest: #{path.inspect}"
35
+ end
36
+
37
+ def target_path(source_path)
38
+ @package.build_path(source_path) ||
39
+ @package.register(source_path, File.join("#{target_name}.d", File.basename(source_path)))
40
+ end
41
+
42
+ def target_file(name, content=nil)
43
+ tempfile = @package.build File.join("#{target_name}.d", name)
44
+
45
+ tempfile << content if content
46
+ yield(tempfile) if block_given?
47
+
48
+ target_path tempfile.path
49
+ end
50
+
51
+ def attrs
52
+ @attributes.current
53
+ end
54
+
55
+ def attributes(attributes_name)
56
+ path = source_path('attributes', "#{attributes_name}.rb")
57
+
58
+ @attributes.instance_eval(File.read(path), path)
59
+ @attributes.reset(false)
60
+ self
61
+ end
62
+
63
+ def helpers(helper_name)
64
+ require Utils.underscore(helper_name)
65
+ extend Utils.constantize(helper_name)
66
+ end
67
+
68
+ def evaluate(recipe_name=target_name)
69
+ path = source_path('recipes', "#{recipe_name}.rb")
70
+ instance_eval(File.read(path), path)
71
+ self
72
+ end
73
+
74
+ def file_path(file_name)
75
+ path = source_path('files', file_name)
76
+ target_path path
77
+ end
78
+
79
+ def capture_path(name, &block)
80
+ content = capture(false) { instance_eval(&block) }
81
+ target_file(name, content)
82
+ end
83
+
84
+ def recipe_path(recipe_name, target_name = recipe_name)
85
+ source_path =
86
+ @package.built?(target_name) ?
87
+ @package.source_path(target_name) :
88
+ Recipe.new(target_name, @package).evaluate(recipe_name).target.path
89
+
90
+ target_path source_path
91
+ end
92
+
93
+ def template_path(template_name, locals={})
94
+ path = source_path('templates', "#{template_name}.erb")
95
+ target_file template_name, Template.build(File.read(path), locals, path)
96
+ end
97
+
98
+ def close
99
+ @package.close
100
+ @package.registry
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,145 @@
1
+ require 'erb'
2
+
3
+ # Generated by Linecook, do not edit.
4
+ module Linecook
5
+ module Shell
6
+ module Posix
7
+ # :stopdoc:
8
+ COMMENT_LINE = __LINE__ + 2
9
+ COMMENT = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
10
+ # <%= str %>
11
+ END_OF_TEMPLATE
12
+ # :startdoc:
13
+
14
+ # Writes a comment
15
+ def comment(str)
16
+ eval(COMMENT, binding, __FILE__, COMMENT_LINE)
17
+ nil
18
+ end
19
+
20
+ def _comment(*args, &block) # :nodoc:
21
+ capture { comment(*args, &block) }
22
+ end
23
+
24
+ # :stopdoc:
25
+ HEREDOC_LINE = __LINE__ + 2
26
+ HEREDOC = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
27
+ <<<%= options[:indent] ? '-' : ' '%><%= options[:quote] ? "\"#{delimiter}\"" : delimiter %>
28
+ <% yield %>
29
+ <%= delimiter %>
30
+
31
+ END_OF_TEMPLATE
32
+ # :startdoc:
33
+
34
+ # Makes a heredoc statement surrounding the contents of the block. Options:
35
+
36
+ #
37
+
38
+ # delimiter the delimiter used, by default HEREDOC_n where n increments
39
+
40
+ # indent add '-' before the delimiter
41
+
42
+ # quote quotes the delimiter
43
+
44
+ def heredoc(options={})
45
+ delimiter = options[:delimiter] || begin
46
+ @heredoc_count ||= -1
47
+ "HEREDOC_#{@heredoc_count += 1}"
48
+ end
49
+ eval(HEREDOC, binding, __FILE__, HEREDOC_LINE)
50
+ nil
51
+ end
52
+
53
+ def _heredoc(*args, &block) # :nodoc:
54
+ capture { heredoc(*args, &block) }
55
+ end
56
+
57
+ # :stopdoc:
58
+ NOT_IF_LINE = __LINE__ + 2
59
+ NOT_IF = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
60
+ only_if("! #{cmd}", &block)
61
+ END_OF_TEMPLATE
62
+ # :startdoc:
63
+
64
+ def not_if(cmd, &block)
65
+ eval(NOT_IF, binding, __FILE__, NOT_IF_LINE)
66
+ nil
67
+ end
68
+
69
+ def _not_if(*args, &block) # :nodoc:
70
+ capture { not_if(*args, &block) }
71
+ end
72
+
73
+ # :stopdoc:
74
+ ONLY_IF_LINE = __LINE__ + 2
75
+ ONLY_IF = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
76
+ if <%= cmd %>
77
+ then
78
+ <% indent { yield } %>
79
+ fi
80
+
81
+ END_OF_TEMPLATE
82
+ # :startdoc:
83
+
84
+ def only_if(cmd)
85
+ eval(ONLY_IF, binding, __FILE__, ONLY_IF_LINE)
86
+ nil
87
+ end
88
+
89
+ def _only_if(*args, &block) # :nodoc:
90
+ capture { only_if(*args, &block) }
91
+ end
92
+
93
+ # :stopdoc:
94
+ SET_LINE = __LINE__ + 2
95
+ SET = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
96
+ <% if block_given? %>
97
+ <% reset_file = "LINECOOK_RESET_OPTIONS_#{next_count}" %>
98
+ <%= reset_file %>=`mktemp /tmp/line_cook_reset_fileXXXXXX`
99
+ set -o | sed 's/\(.*\) on/set -o \1/' | sed 's/\(.*\) off/set +o \1/' > $<%= reset_file %>
100
+ <% end %><% options.keys.sort_by {|opt| opt.to_s }.each do |opt| %>
101
+ set <%= options[opt] ? '-' : '+' %>o <%= opt %>
102
+ <% end %>
103
+ <% if block_given? %>
104
+
105
+ <% indent { yield } %>
106
+
107
+ source $<%= reset_file %>
108
+ <% end %>
109
+
110
+ END_OF_TEMPLATE
111
+ # :startdoc:
112
+
113
+ # Sets bash options for the duration of a block. If no block is given,
114
+
115
+ # set simply sets the options as specified.
116
+ def set(options)
117
+ eval(SET, binding, __FILE__, SET_LINE)
118
+ nil
119
+ end
120
+
121
+ def _set(*args, &block) # :nodoc:
122
+ capture { set(*args, &block) }
123
+ end
124
+
125
+ # :stopdoc:
126
+ UNSET_LINE = __LINE__ + 2
127
+ UNSET = "self." + ERB.new(<<'END_OF_TEMPLATE', nil, '<>').src
128
+ <% keys.each do |key| %>
129
+ unset <%= key %>
130
+ <% end %>
131
+ END_OF_TEMPLATE
132
+ # :startdoc:
133
+
134
+ # Unsets a list of variables.
135
+ def unset(*keys)
136
+ eval(UNSET, binding, __FILE__, UNSET_LINE)
137
+ nil
138
+ end
139
+
140
+ def _unset(*args, &block) # :nodoc:
141
+ capture { unset(*args, &block) }
142
+ end
143
+ end
144
+ end
145
+ end