Class: Linecook::Package

Inherits:
Object
  • Object
show all
Defined in:
lib/linecook/package.rb

Constant Summary collapse

CONTEXT_KEY =
'linecook'
COOKBOOK_KEY =
'cookbook'
MANIFEST_KEY =
'manifest'
PACKAGE_KEY =
'package'
REGISTRY_KEY =
'registry'
FILES_KEY =
'files'
TEMPLATES_KEY =
'templates'
RECIPES_KEY =
'recipes'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env = {}) ⇒ Package

Returns a new instance of Package.



61
62
63
64
65
66
# File 'lib/linecook/package.rb', line 61

def initialize(env={})
  @env = env
  @tempfiles = []
  @counters  = Hash.new(0)
  @callbacks = Hash.new {|hash, key| hash[key] = StringIO.new }
end

Instance Attribute Details

#callbacksObject (readonly)

A hash of callbacks registered with self



59
60
61
# File 'lib/linecook/package.rb', line 59

def callbacks
  @callbacks
end

#countersObject (readonly)

A hash of counters used by variable.



56
57
58
# File 'lib/linecook/package.rb', line 56

def counters
  @counters
end

#envObject (readonly)

The package environment



50
51
52
# File 'lib/linecook/package.rb', line 50

def env
  @env
end

#tempfilesObject (readonly)

An array of tempfiles generated by self (used to cleanup on close)



53
54
55
# File 'lib/linecook/package.rb', line 53

def tempfiles
  @tempfiles
end

Class Method Details

.init(package_file = nil, project_dir = nil) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/linecook/package.rb', line 24

def init(package_file=nil, project_dir=nil)
  cookbook = Cookbook.init(project_dir)
  package  = setup(package_file, cookbook)
  
  if package_file
    package.context[PACKAGE_KEY] ||= begin
      name = File.basename(package_file).chomp(File.extname(package_file)) 
      {'recipes' => { 'run' => name }}
    end
  end
  
  package
end

.setup(env, cookbook = nil) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/linecook/package.rb', line 9

def setup(env, cookbook=nil)
  unless env.kind_of?(Hash)
    env = Utils.load_config(env)
  end
  
  package = new(env)
  
  if cookbook
    manifest = package.manifest
    manifest.replace cookbook.manifest
  end
  
  package
end

Instance Method Details

#attributes_path(attributes_name) ⇒ Object

Returns the resource_path the named attributes file (ex ‘attributes/name.rb’).



181
182
183
# File 'lib/linecook/package.rb', line 181

def attributes_path(attributes_name)
  resource_path('attributes', attributes_name)
end

#buildObject

Builds the files, templates, and recipes for self. Returns self.



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/linecook/package.rb', line 297

def build
  files.each do |target_name, file_name|
    build_file(target_name, *file_name)
  end
  
  templates.each do |target_name, template_name|
    build_template(target_name, *template_name)
  end
  
  recipes.each do |target_name, recipe_name|
    build_recipe(target_name, *recipe_name)
  end
  
  self
end

#build_file(target_name, file_name = target_name, mode = 0600) ⇒ Object

Looks up the file with the specified name using file_path and registers it to target_name. Raises an error if the target is already registered.



266
267
268
269
# File 'lib/linecook/package.rb', line 266

def build_file(target_name, file_name=target_name, mode=0600)
  register target_name, file_path(file_name), mode
  self
end

#build_recipe(target_name, recipe_name = target_name, mode = 0700) ⇒ Object

Looks up the recipe with the specified name using recipe_path, evaluates it, and registers the result to target_name. Raises an error if the target is already registered. Returns self.



287
288
289
290
291
292
293
294
# File 'lib/linecook/package.rb', line 287

def build_recipe(target_name, recipe_name=target_name, mode=0700)
  path = recipe_path(recipe_name)
  recipe = setup_recipe(target_name, mode)
  recipe.instance_eval(File.read(path), path)
  recipe.close
  
  self
end

#build_template(target_name, template_name = target_name, mode = 0600, locals = {'attrs' => env}) ⇒ Object

Looks up the template with the specified name using template_path, builds, and registers it to target_name. The locals will be set for access in the template context. Raises an error if the target is already registered. Returns self.



275
276
277
278
279
280
281
282
# File 'lib/linecook/package.rb', line 275

def build_template(target_name, template_name=target_name, mode=0600, locals={'attrs' => env})
  content = load_template(template_name).build(locals)
  
  target = setup_tempfile(target_name, mode)
  target << content
  target.close
  self
end

#closeObject

Closes all tempfiles and returns self.



335
336
337
338
339
340
# File 'lib/linecook/package.rb', line 335

def close
  tempfiles.each do |tempfile|
    tempfile.close unless tempfile.closed?
  end
  self
end

#configObject

Returns the linecook configs in env, as keyed by CONFIG_KEY. Defaults to an empty hash.



89
90
91
# File 'lib/linecook/package.rb', line 89

def config
  context[PACKAGE_KEY] ||= {}
end

#content(target_name, length = nil, offset = nil) ⇒ Object

Returns the content of the source_path for target_name, as registered in self. Returns nil if the target is not registered.



315
316
317
318
# File 'lib/linecook/package.rb', line 315

def content(target_name, length=nil, offset=nil)
  path = source_path(target_name)
  path ? File.read(path, length, offset) : nil
end

#contextObject

Returns the linecook context in env, as keyed by CONTEXT_KEY. Defaults to an empty hash.



70
71
72
# File 'lib/linecook/package.rb', line 70

def context
  env[CONTEXT_KEY] ||= {}
end

#export(dir, options = {}) ⇒ Object

Closes self and exports the registry to dir by copying or moving the registered source paths to the target path under dir. By default tempfiles are moved while all other files are copied.

Returns registry, which is re-written to reflect the new source paths.



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/linecook/package.rb', line 357

def export(dir, options={})
  close
  
  options = {
    :allow_move => true
  }.merge(options)
  
  allow_move = options[:allow_move]
  
  if File.exists?(dir)
    FileUtils.rm_r(dir)
  end
  
  registry.each_key do |target_name|
    export_path = File.join(dir, target_name)
    export_dir  = File.dirname(export_path)
    source_path, mode = registry[target_name]
    
    next if source_path == export_path
    
    unless File.exists?(export_dir)
      FileUtils.mkdir_p(export_dir)
    end
    
    if allow_move && tempfile?(source_path)
      FileUtils.mv(source_path, export_path)
    else
      FileUtils.cp(source_path, export_path)
    end
    
    FileUtils.chmod(mode, export_path)
    
    registry[target_name] = [export_path, mode]
  end
  
  tempfiles.clear
  registry
end

#file_path(file_name) ⇒ Object

Returns the resource_path the named file (ex ‘files/name’)



186
187
188
# File 'lib/linecook/package.rb', line 186

def file_path(file_name)
  resource_path('files', file_name)
end

#filesObject

Returns the hash of (source, target) pairs identifying which of the files will be built into self by build. Files are identified by FILES_KEY in config, and normalized the same way as recipes.



96
97
98
# File 'lib/linecook/package.rb', line 96

def files
  config[FILES_KEY] = Utils.hashify(config[FILES_KEY])
end

#load_attributes(attributes_name = nil) ⇒ Object

Loads the named attributes file into and returns an instance of Attributes. The loading mechanism depends on the attributes file extname.

.rb: evaluate in the context of attributes
.yml,.yaml,.json: load as YAML and merge into attributes

All other file types raise an error. Simply returns a new Attributes instance if no name is given.



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/linecook/package.rb', line 209

def load_attributes(attributes_name=nil)
  attributes = Attributes.new
  
  if attributes_name
    path = attributes_path(attributes_name)
    
    case File.extname(path)
    when '.rb'
      attributes.instance_eval(File.read(path), path)
    when '.yml', '.yaml', '.json'
      attributes.attrs.merge!(YAML.load_file(path))
    else
      raise "invalid attributes format: #{path.inspect}"
    end
  end
  
  attributes
end

#load_helper(helper_name) ⇒ Object

Loads and returns the helper constant specified by helper_name. The helper_name is underscored to determine a require path and camelized to determine the constant name.



237
238
239
240
# File 'lib/linecook/package.rb', line 237

def load_helper(helper_name)
  require Utils.underscore(helper_name)
  Utils.constantize(helper_name)
end

#load_template(template_name) ⇒ Object

Load the template file with the specified name and wraps as a Template. Returns the new Template object.



230
231
232
# File 'lib/linecook/package.rb', line 230

def load_template(template_name)
  Template.new template_path(template_name)
end

#manifestObject

Returns the manifest in config, as keyed by MANIFEST_KEY. Defaults to an empty hash.



76
77
78
# File 'lib/linecook/package.rb', line 76

def manifest
  context[MANIFEST_KEY] ||= {}
end

#mode(target_name) ⇒ Object

Returns the mode for target_name, as registered in self. Returns nil if the target is not registered.



329
330
331
332
# File 'lib/linecook/package.rb', line 329

def mode(target_name)
  entry = registry[target_name]
  entry ? entry[1] : nil
end

#next_target_name(target_name = 'file') ⇒ Object

Increments target_name until an unregistered name is found and returns the result.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/linecook/package.rb', line 141

def next_target_name(target_name='file')
  count = 0
  registry.each_key do |key|
    if key.index(target_name) == 0
      count += 1
    end
  end
  
  if count > 0
    target_name = "#{target_name}.#{count}"
  end
  
  target_name
end

#next_variable_name(context) ⇒ Object

Returns a package-unique variable with base ‘name’.



157
158
159
160
161
162
163
164
# File 'lib/linecook/package.rb', line 157

def next_variable_name(context)
  context = context.to_s
  
  count = counters[context]
  counters[context] += 1
  
  "#{context}#{count}"
end

#recipe_path(recipe_name) ⇒ Object

Returns the resource_path the named recipe file (ex ‘recipes/name.rb’).



196
197
198
# File 'lib/linecook/package.rb', line 196

def recipe_path(recipe_name)
  resource_path('recipes', recipe_name)
end

#recipesObject

Returns the hash of (source, target) pairs identifying which recipes will be built into self by build. Recipes are identified by RECIPES_KEY in config.

Non-hash recipes are normalized by expanding arrays into a redundant hash, such that each entry has the same source and target (more concretely, the ‘example’ recipe is registered as the ‘example’ script). Strings are split along colons into an array and then expanded.

For example:

package = Package.new('linecook' => {'package' => {'recipes' => 'a:b:c'}})
package.recipes   # => {'a' => 'a', 'b' => 'b', 'c' => 'c'}


121
122
123
# File 'lib/linecook/package.rb', line 121

def recipes
  config[RECIPES_KEY] = Utils.hashify(config[RECIPES_KEY])
end

#register(target_name, source_path, mode = 0600) ⇒ Object

Registers the source_path to target_name in the registry and revese_registry. Raises an error if the source_path is already registered.



128
129
130
131
132
133
134
135
136
137
# File 'lib/linecook/package.rb', line 128

def register(target_name, source_path, mode=0600)
  source_path = File.expand_path(source_path)
  
  if registry.has_key?(target_name) && registry[target_name] != [source_path, mode]
    raise "already registered: #{target_name} (%s, %o)" % registry[target_name]
  end
  
  registry[target_name] = [source_path, mode]
  target_name
end

#registryObject

Returns the registry in config, as keyed by REGISTRY_KEY. Defaults to an empty hash. A hash of (target_name, source_path) pairs identifying files that should be included in a package



83
84
85
# File 'lib/linecook/package.rb', line 83

def registry
  context[REGISTRY_KEY] ||= {}
end

#resetObject

Closes and clears all tempfiles, the registry, callbacks, and counters.



343
344
345
346
347
348
349
350
# File 'lib/linecook/package.rb', line 343

def reset
  close
  registry.clear
  tempfiles.clear
  callbacks.clear
  counters.clear
  self
end

#resource?(type, path) ⇒ Boolean

Returns true if there is a path for the specified resource in manifest.

Returns:

  • (Boolean)


167
168
169
170
# File 'lib/linecook/package.rb', line 167

def resource?(type, path)
  resources = manifest[type]
  resources && resources.has_key?(path)
end

#resource_path(type, path) ⇒ Object

Returns the path to the resource in manfiest. Raises an error if there is no such resource.



174
175
176
177
178
# File 'lib/linecook/package.rb', line 174

def resource_path(type, path)
  resources = manifest[type]
  resource_path = resources ? resources[path] : nil
  resource_path or raise "no such resource in manifest: #{type.inspect} #{path.inspect}"
end

#setup_recipe(target_name = next_target_name, mode = 0700) ⇒ Object

Returns a recipe bound to self.



243
244
245
# File 'lib/linecook/package.rb', line 243

def setup_recipe(target_name = next_target_name, mode=0700)
  Recipe.new(self, target_name, mode)
end

#setup_tempfile(target_name = next_target_name, mode = 0600) ⇒ Object

Generates a tempfile for the target path and registers it with self. As with register, the target_name will be incremented as needed. Returns the open tempfile.



250
251
252
253
254
255
256
257
# File 'lib/linecook/package.rb', line 250

def setup_tempfile(target_name = next_target_name, mode=0600)
  tempfile = Tempfile.new File.basename(target_name)
  
  register(target_name, tempfile.path, mode)
  tempfiles << tempfile
  
  tempfile
end

#source_path(target_name) ⇒ Object

Returns the source_path for target_name, as registered in self. Returns nil if the target is not registered.



322
323
324
325
# File 'lib/linecook/package.rb', line 322

def source_path(target_name)
  entry = registry[target_name]
  entry ? entry[0] : nil
end

#tempfile?(source_path) ⇒ Boolean

Returns true if the source_path is for a tempfile generated by self.

Returns:

  • (Boolean)


260
261
262
# File 'lib/linecook/package.rb', line 260

def tempfile?(source_path)
  tempfiles.any? {|tempfile| tempfile.path == source_path }
end

#template_path(template_name) ⇒ Object

Returns the resource_path the named template file (ex ‘templates/name.erb’).



191
192
193
# File 'lib/linecook/package.rb', line 191

def template_path(template_name)
  resource_path('templates', template_name)
end

#templatesObject

Returns the hash of (source, target) pairs identifying which templates will be built into self by build. Templates are identified by TEMPLATES_KEY in config, and normalized the same way as recipes.



103
104
105
# File 'lib/linecook/package.rb', line 103

def templates
  config[TEMPLATES_KEY] = Utils.hashify(config[TEMPLATES_KEY])
end