Module: Ferb

Defined in:
lib/ferb.rb

Overview

This is a mixin module that makes it easy to define methods on a class that are expanded using an ERB template

Author

Jim Powers ([email protected])

Copyright

Copyright © 2008-2009 Jim Powers

License

Distributed under the terms of the LGPLv3.0 or newer.

For details see the LGPL30 file

Example:

class Foo

require 'ferb'
include Ferb

def_template("hello_world(m)","<b><%= m %></b>")

end

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.build_internal_template(signature, body) ⇒ Object



48
49
50
51
52
53
54
55
# File 'lib/ferb.rb', line 48

def self.build_internal_template(signature,body)
  body_rb = ERB.new(body,nil,'-').src
  <<EOS
def __ferb_#{signature}
#{body_rb}
end
EOS
end

.build_template(signature, clean_sig, source_file_data = nil, function = nil) ⇒ Object

Builds a string representing the definition of the function



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/ferb.rb', line 59

def self.build_template(signature, clean_sig, source_file_data = nil, function = nil)
  unless source_file_data
  <<EOS
def #{signature}
__ferb_#{clean_sig}
end
EOS
  else
  <<EOS
def #{signature}
if Ferb.enable_reload?
  full_path = '#{source_file_data[:full_path]}'
  signature = '#{function}'
  if should_reload?(full_path)
    args = {:path => full_path}
    template = Ferb.load_template(args)
    add_timestamp(args[:full_path], args[:timestamp])
    internal_func = Ferb.construct_internal_function_def(signature,template)
    eval(internal_func)
  end
end
__ferb_#{clean_sig}
end
EOS
  end
end

.construct_function_def(function, template, args = nil) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
# File 'lib/ferb.rb', line 134

def self.construct_function_def(function, template, args = nil)
  parts = get_template_parts(template)
  funsig = nil
  clean_sig = nil
  if parts.is_a?(Array)
    funsig, clean_sig = construct_function_sig(function,parts[0])
  else
    funsig = function
  end
  build_template(funsig, clean_sig, args, function)
end

.construct_function_sig(function, sig) ⇒ Object



125
126
127
128
129
130
131
132
# File 'lib/ferb.rb', line 125

def self.construct_function_sig(function, sig)
  sig = sig.strip
  if !sig.empty?
    ["#{function.to_s.strip}(#{sig})", "#{function.to_s.strip}(#{MethodArgsHelper.get_args(sig).join(',')})"]
  else
    [function.to_s.strip, nil]
  end
end

.construct_internal_function_def(function, template) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/ferb.rb', line 146

def self.construct_internal_function_def(function, template)
  parts = get_template_parts(template)
  funsig = nil
  clean_sig = nil
  body = nil
  if parts.is_a?(Array)
    funsig, clean_sig = construct_function_sig(function,parts[0])
    body = parts[1]
  else
    funsig = function
    body = template
  end
  build_internal_template(funsig, body)
end

.enable_reload=(val) ⇒ Object

Enables support for reloading file templates at run-time if the file timestamp has changed



37
38
39
# File 'lib/ferb.rb', line 37

def self.enable_reload=(val)
  @@enable_reload = val
end

.enable_reload?Boolean

Enables support for reloading file templates at run-time if the file timestamp has changed

Returns:

  • (Boolean)


27
28
29
30
31
32
33
# File 'lib/ferb.rb', line 27

def self.enable_reload?
  if defined?(@@enable_reload)
    return @@enable_reload
  else
    return false
  end
end

.get_template_parts(template) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ferb.rb', line 109

def self.get_template_parts(template)
  result = nil
  if template and !template.strip.empty?
    strio = StringIO.new(template)
    first_line = strio.readline
    if first_line.strip =~ /^<!--.*\|(.*)\|.*-->$/
      sig = $1
      body = strio.read
      result = [sig, body]
    else
      body = template
    end
  end
  result
end

.included(base) ⇒ Object

The “callback” when this module is included by a class. Extends the class with the metods defined in the inner ‘ClassMethods’ module



44
45
46
# File 'lib/ferb.rb', line 44

def self.included(base)
  base.extend(ClassMethods)
end

.load_template(args) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ferb.rb', line 86

def self.load_template(args)
  result = nil
  path = nil
  if args.has_key?(:path)
    path = Pathname.new(args[:path].to_s.strip)
  elsif args.has_key?(:file)
    template_root = args[:template_root]
    unless template_root.is_a?(Pathname)
      template_root = Pathname.new(args[:template_root].to_s)
    end
    args[:template_root] = template_root
    path = template_root+args[:file]
  else
    raise(":file or :path not specified: #{args.inspect}")
  end
  args[:full_path] = path.realpath
  args[:timestamp] = path.mtime
  path.open do |f|
    result = f.read
  end
  result
end

Instance Method Details

#add_timestamp(path, time) ⇒ Object

Record file timestamps



241
242
243
# File 'lib/ferb.rb', line 241

def add_timestamp(path, time)
  self.file_timestamps[path] = time
end

#def_template(signature, args) ⇒ Object

Defines a template function in an individual object (only available to a prticular instance) as an instance method. Creates the method directly on the object (instance).

Arguments:

<tt>signature</tt> - (string) The complete signature including parameters
<tt>body</tt> - (string) The body of the functionusing ERB syntax

Example:

obj.def_template(“hello_world(message)”,“<%= message %>”)



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/ferb.rb', line 286

def def_template(signature,args)
  if args.is_a?(String)
    internal_func = Ferb.construct_internal_function_def(signature,args)
    external_func = Ferb.construct_function_def(signature,args)
    eval(internal_func)
    eval(external_func)
  elsif args.is_a?(Hash)
    args = { :template_root => template_root}.merge(args)
    template = Ferb.load_template(args)
    add_timestamp(args[:full_path], args[:timestamp])
    internal_func = Ferb.construct_internal_function_def(signature,template)
    external_func = Ferb.construct_function_def(signature,template,args)
    eval(internal_func)
    eval(external_func)
  end
end

#file_timestampsObject



233
234
235
236
237
238
# File 'lib/ferb.rb', line 233

def file_timestamps
  unless defined?(@file_timestamps)
    @file_timestamps = {}
  end
  @file_timestamps
end

#should_reload?(path) ⇒ Boolean

Test file timestamps

Returns:

  • (Boolean)


246
247
248
249
250
251
252
253
254
# File 'lib/ferb.rb', line 246

def should_reload?(path)
  time = Pathname.new(path).mtime
  ts = self.file_timestamps[path]
  if ts and (ts >= time)
    return false;
  else
    return true
  end
end

#template_rootObject

Returns the root in the file system where ERB templates can be found



257
258
259
260
261
262
263
264
265
266
# File 'lib/ferb.rb', line 257

def template_root
  if !defined?(@template_root)
    if self.class.respond_to?(:template_root)
      @template_root = self.class.template_root
    else
      @template_root = Pathname.new(File.expand_path(File.dirname(__FILE__)))
    end
  end
  @template_root
end

#template_root=(location) ⇒ Object

Sets the root in the file system where ERB templates can be found



269
270
271
# File 'lib/ferb.rb', line 269

def template_root=(location)
  @template_root = Pathname.new(File.expand_path(location))
end