linecook 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
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