Class: Linecook::Recipe

Inherits:
BasicObject
Defined in:
lib/linecook/recipe.rb

Overview

Recipe is the context in which recipes are evaluated (literally). Recipe uses compiled ERB snippets to build text using method calls. For example:

module Helper
  # This is an ERB template compiled to write to a Recipe.
  #
  #   compiler = ERB::Compiler.new('<>')
  #   compiler.put_cmd = "write"
  #   compiler.insert_cmd = "write"
  #   compiler.compile("echo '<%= args.join(' ') %>'\n")
  #
  def echo(*args)
    write "echo '"; write(( args.join(' ') ).to_s); write "'\n"
  end
end

recipe = Recipe.new do
  _extend_ Helper
  echo 'a', 'b c'
  echo 'X Y'.downcase, :z
end

"\n" + recipe.to_s
# => %{
# echo 'a b c'
# echo 'x y z'
# }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(package = Package.new, cookbook = Cookbook.new, target = "") ⇒ Recipe

Returns a new instance of Recipe.



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/linecook/recipe.rb', line 55

def initialize(package = Package.new, cookbook = Cookbook.new, target = "")
  @_proxy_    = Proxy.new(self)
  @_package_  = package
  @_cookbook_ = cookbook
  @_package_path_ = nil
  @_package_opts_ = nil
  @target     = target
  @chain      = false
  @attributes = {}
  @attrs      = nil

  if Kernel.block_given?
    instance_eval(&Proc.new)
  end
end

Instance Attribute Details

#_cookbook_Object (readonly)

The recipe Cookbook



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

def _cookbook_
  @_cookbook_
end

#_package_Object (readonly)

The recipe Package



43
44
45
# File 'lib/linecook/recipe.rb', line 43

def _package_
  @_package_
end

#_package_opts_Object (readonly)

Returns the value of attribute package_opts.



47
48
49
# File 'lib/linecook/recipe.rb', line 47

def _package_opts_
  @_package_opts_
end

#_package_path_Object (readonly)

Returns the value of attribute package_path.



45
46
47
# File 'lib/linecook/recipe.rb', line 45

def _package_path_
  @_package_path_
end

#_proxy_Object (readonly)

The recipe Proxy



40
41
42
# File 'lib/linecook/recipe.rb', line 40

def _proxy_
  @_proxy_
end

#targetObject (readonly)

The target recieving writes



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

def target
  @target
end

Class Method Details

.const_missing(name) ⇒ Object

Overridden to look up constants as normal.



72
73
74
# File 'lib/linecook/recipe.rb', line 72

def self.const_missing(name)
  ::Object.const_get(name)
end

Instance Method Details

#_(str = nil, &block) ⇒ Object

Returns a child of self with a new target. Writes str to the child, and evaluates the block in the context of the child, if given.



147
148
149
150
151
152
# File 'lib/linecook/recipe.rb', line 147

def _(str=nil, &block)
  child = _beget_
  child.write str if str
  child.instance_eval(&block) if block
  child
end

#_beget_Object

Returns a clone of self created by clone, but also calls initialize_child on the clone.



139
140
141
142
143
# File 'lib/linecook/recipe.rb', line 139

def _beget_
  clone = _clone_
  clone._initialize_child_(self)
  clone
end

#_class_Object

Returns the class for self.



91
92
93
# File 'lib/linecook/recipe.rb', line 91

def _class_
  _singleton_class_.superclass
end

#_clone_Object

Returns a clone of self, kind of like Object#clone.

Note that unlike Object.clone this currently does not carry forward tainted/frozen state, nor can it carry forward singleton methods. Modules and internal state only.



119
120
121
122
123
124
# File 'lib/linecook/recipe.rb', line 119

def _clone_
  clone = _class_.allocate
  clone._initialize_clone_(self)
  _singleton_class_.included_modules.each {|mod| clone._extend_ mod }
  clone
end

#_extend_(mod) ⇒ Object

Extends self with the module.



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

def _extend_(mod)
  mod.__send__(:extend_object, self)
end

#_initialize_child_(orig) ⇒ Object

Callback to initialize children created by beget. Sets a new target by calling dup.clear on the original target, and unchains. Note that the child shares the same attributes as the parent, and so can (un)intentionally cause changes in the parent.



130
131
132
133
134
135
# File 'lib/linecook/recipe.rb', line 130

def _initialize_child_(orig)
  @target = orig.target.dup.clear
  @_package_path_ = nil
  @_package_opts_ = nil
  unchain
end

#_initialize_clone_(orig) ⇒ Object

Callback to initialize a clone of self. Passes forward all state, including local data and attributes.



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/linecook/recipe.rb', line 102

def _initialize_clone_(orig)
  @_proxy_    = Proxy.new(self)
  @_package_  = orig._package_
  @_cookbook_ = orig._cookbook_
  @_package_path_ = orig._package_path_
  @_package_opts_ = orig._package_path_
  @target     = orig.target
  @chain      = orig.chain?
  @attributes = orig.attributes
  @attrs      = nil
end

#_singleton_class_Object

Returns the singleton class for self. Used by clone to access modules included in self (ex via extend).



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/linecook/recipe.rb', line 78

def _singleton_class_
  class << self
    SINGLETON_CLASS = self
    def _singleton_class_
      SINGLETON_CLASS
    end
  end

  # this and future calls go to the _singleton_class_ as defined above.
  _singleton_class_
end

#attributes(source_name = nil, &block) ⇒ Object

Loads the specified attributes file and merges the results into attrs. A block may be given to specify attrs as well; it will be evaluated in the context of an Attributes instance.

Returns a hash representing all attributes loaded thusfar (specifically attrs prior to merging in the package env). The attributes hash should be treated as if it were read-only.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/linecook/recipe.rb', line 171

def attributes(source_name=nil, &block)
  if source_name || block
    attributes = Attributes.new

    if source_name
      if source_path = _cookbook_.find(:attributes, source_name, attributes.load_attrs_extnames)
        attributes.load_attrs(source_path)
      end
    end

    if block
      attributes.instance_eval(&block)
    end

    @attributes = Utils.deep_merge(@attributes, attributes.to_hash)
    @attrs = nil
  end

  @attributes
end

#attrsObject

Returns the package env merged over any attrs specified by attributes.

The attrs hash should be treated as if it were read-only because changes could alter the package env and thereby spill over into other recipes.



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

def attrs
  @attrs ||= Utils.deep_merge(@attributes, _package_.env)
end

#capture(target = "") ⇒ Object

Captures writes during the block to a new target. Returns the target.



224
225
226
227
228
229
230
231
232
233
# File 'lib/linecook/recipe.rb', line 224

def capture(target = "")
  current = @target
  begin
    @target = target
    yield
  ensure
    @target = current
  end
  target
end

#capture_to(package_path, options = {}) ⇒ Object



235
236
237
238
# File 'lib/linecook/recipe.rb', line 235

def capture_to(package_path, options={})
  str = capture { yield }
  _package_.add(package_path, options) {|io| io << str }
end

#chainObject

Causes chain? to return true. Returns self.



261
262
263
264
# File 'lib/linecook/recipe.rb', line 261

def chain
  @chain = true
  self
end

#chain?Boolean

Returns true as per chain/unchain.

Returns:

  • (Boolean)


280
281
282
# File 'lib/linecook/recipe.rb', line 280

def chain?
  @chain
end

#chain_proxyObject

Returns the proxy. Unchains first to ensure that if the proxy is not called, then the previous chain is stopped.



274
275
276
277
# File 'lib/linecook/recipe.rb', line 274

def chain_proxy
  unchain
  _proxy_
end

#helpers(*helper_names) ⇒ Object

Looks up and extends self with the specified helper(s).



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/linecook/recipe.rb', line 201

def helpers(*helper_names)
  helper_names.each do |helper|
    unless helper.respond_to?(:extend_object)
      helper_name = helper
      module_name = Utils.camelize(helper_name)

      helper = Utils.constantize(module_name) do
        # Don't use Kernel because that may evade RubyGems
        Utils.__send__(:require, Utils.underscore(helper_name))
        Utils.constantize(module_name)
      end
    end
    helper.__send__(:extend_object, self)
  end
  self
end

#register(package_path, options = {}) ⇒ Object



218
219
220
221
# File 'lib/linecook/recipe.rb', line 218

def register(package_path, options={})
  source_path = _cookbook_.find(:files, options[:source] || package_path)
  _package_.register(package_path, source_path, options)
end

#register_as(path, options = {}) ⇒ Object



154
155
156
157
158
# File 'lib/linecook/recipe.rb', line 154

def register_as(path, options={})
  @_package_path_ = path
  @_package_opts_ = options
  self
end

#register_to(package, path = _package_path_, options = _package_opts_) ⇒ Object



160
161
162
# File 'lib/linecook/recipe.rb', line 160

def register_to(package, path = _package_path_, options = _package_opts_)
  package.add(path, options) {|io| io << to_s }
end

#render(template_name, locals = attrs) ⇒ Object

Looks up a template in cookbook and renders it.



255
256
257
258
# File 'lib/linecook/recipe.rb', line 255

def render(template_name, locals=attrs)
  file = _cookbook_.find(:templates, template_name, ['.erb'])
  Tilt.new(file).render(Object.new, locals)
end

#to_sObject

Returns target.to_s.



285
286
287
# File 'lib/linecook/recipe.rb', line 285

def to_s
  target.to_s
end

#unchainObject

Causes chain? to return false. Returns self.



267
268
269
270
# File 'lib/linecook/recipe.rb', line 267

def unchain
  @chain = false
  self
end

#write(input) ⇒ Object

Writes input to target using ‘<<`. Stringifies input using to_s. Returns self.



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

def write(input)
  target << input.to_s
  self
end

#writeln(input = nil) ⇒ Object

Writes input to self, writes a newline, and returns last.



248
249
250
251
252
# File 'lib/linecook/recipe.rb', line 248

def writeln(input=nil)
  write input
  write "\n"
  self
end