Module: WLang

Defined in:
lib/wlang.rb,
lib/wlang/rule.rb,
lib/wlang/parser.rb,
lib/wlang/errors.rb,
lib/wlang/encoder.rb,
lib/wlang/version.rb,
lib/wlang/dialect.rb,
lib/wlang/rule_set.rb,
lib/wlang/template.rb,
lib/wlang/hash_scope.rb,
lib/wlang/dialect_dsl.rb,
lib/wlang/encoder_set.rb,
lib/wlang/parser_state.rb,
lib/wlang/wlang_command.rb,
lib/wlang/dialect_loader.rb,
lib/wlang/hosted_language.rb,
lib/wlang/intelligent_buffer.rb,
lib/wlang/dialects/sql_dialect.rb,
lib/wlang/dialects/yaml_dialect.rb,
lib/wlang/dialects/rdoc_dialect.rb,
lib/wlang/dialects/ruby_dialect.rb,
lib/wlang/wlang_command_options.rb,
lib/wlang/rulesets/basic_ruleset.rb,
lib/wlang/rulesets/ruleset_utils.rb,
lib/wlang/dialects/xhtml_dialect.rb,
lib/wlang/dialects/hosted_dialect.rb,
lib/wlang/rulesets/context_ruleset.rb,
lib/wlang/dialects/coderay_dialect.rb,
lib/wlang/dialects/redcloth_dialect.rb,
lib/wlang/rulesets/encoding_ruleset.rb,
lib/wlang/rulesets/buffering_ruleset.rb,
lib/wlang/dialects/bluecloth_dialect.rb,
lib/wlang/dialects/plain_text_dialect.rb,
lib/wlang/rulesets/imperative_ruleset.rb

Overview

Main module of the wlang code generator/template engine, providing a facade on wlang tools. See also the Roadmap section of README to enter the library.

Defined Under Namespace

Modules: Version Classes: Dialect, Encoder, EncoderSet, Error, EvalError, HashScope, HostedLanguage, IntelligentBuffer, ParseError, Parser, Rule, RuleSet, Template, UndefinedVariableError, WLangCommand

Constant Summary

FILE_EXTENSION_REGEXP =

Regular expression for file extensions

/^\.[a-zA-Z0-9]+$/
DIALECT_NAME_REGEXP_STR =

Reusable string for building dialect name based regexps

"[-a-z]+"
DIALECT_NAME_REGEXP =

Regular expression for dialect names.

/^([-a-z]+)*$/
QUALIFIED_DIALECT_NAME_REGEXP_STR =

Reusable string for building dialect name based regexps

"[-a-z]+([\/][-a-z]+)*"
QUALIFIED_DIALECT_NAME_REGEXP =

Regular expression for dialect qualified names. Dialect qualified names are '/' seperated names, where a name is [-a-z]+.

Examples: wlang/xhtml/uri, wlang/plain-text, …

/^[-a-z]+([\/][-a-z]+)*$/
FILE_EXTENSIONS =

Provides installed extension => dialect mappings. File extensions (keys) contain the first dot (like .wtpl, .whtml, …). Dialects (values) are qualified names, not Dialect instances.

{}
ENCODER_NAME_REGEXP_STR =

Reusable string for building encoder name based regexps

"[-a-z]+"
ENCODER_NAME_REGEXP =

Regular expression for encoder names.

/^([-a-z]+)*$/
QUALIFIED_ENCODER_NAME_REGEXP_STR =

Reusable string for building qualified encoder name based regexps

"[-a-z]+([\/][-a-z]+)*"
QUALIFIED_ENCODER_NAME_REGEXP =

Regular expression for encoder qualified names. Encoder qualified names are '/' seperated names, where a name is [-a-z]+.

Examples: xhtml/entities-encoding, sql/single-quoting, …

/^([-a-z]+)([\/][-a-z]+)*$/
DATA_EXTENSIONS =

Provides installed extension => data loader mapping. File extensions (keys) contain the first dot (like .wtpl, .whtml, …). Data loades are Proc instances that take a single |uri| argument.

{}
VERSION =
Version.to_s

Class Method Summary (collapse)

Class Method Details

+ (Object) check_file_extension(ext)

Checks that ext is a valid file extension or raises an ArgumentError

Raises:

  • (ArgumentError)


30
31
32
33
# File 'lib/wlang.rb', line 30

def self.check_file_extension(ext)
  raise ArgumentError, "Invalid file extension #{ext} (/^\.[a-zA-Z-0-9]+$/ expected)", caller\
    unless FILE_EXTENSION_REGEXP =~ ext
end

+ (Object) check_qualified_dialect_name(name)

Checks that name is a valid qualified dialect name or raises an ArgumentError

Raises:

  • (ArgumentError)


59
60
61
62
# File 'lib/wlang.rb', line 59

def self.check_qualified_dialect_name(name)
  raise ArgumentError, "Invalid dialect qualified name '#{name}' (/^[-a-z]+([\/][-a-z]+)*$/ expected)", caller\
    unless QUALIFIED_DIALECT_NAME_REGEXP =~ name
end

+ (Object) check_qualified_encoder_name(name)

Checks that name is a valid qualified encoder name or raises an ArgumentError

Raises:

  • (ArgumentError)


196
197
198
199
# File 'lib/wlang.rb', line 196

def self.check_qualified_encoder_name(name)
  raise ArgumentError, "Invalid encoder qualified name #{name} (/^[-a-z]+([\/][-a-z]+)*$/ expected)", caller\
    unless QUALIFIED_ENCODER_NAME_REGEXP =~ name
end

+ (Object) check_readable_file(file)

Raises an ArgumentError unless file is a real readable file

Raises:

  • (ArgumentError)


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

def self.check_readable_file(file)
  raise ArgumentError, "File #{file} is not readable or not a file"\
    unless File.exists?(file) and File.file?(file) and File.readable?(file)
end

+ (Object) data_loader(*exts, &block)

Adds a data loader for file extensions. A data loader is a block of arity 1, taking a file as parameter and returning data decoded from the file.

Example:

# We have some MyXMLDataLoader class that is able to create a ruby object
# from things expressed .xml files
WLang::data_loader('.xml') {|file|
  MyXMLDataLaoder.parse_file(file)
}

# Later in a template (see the buffering ruleset that gives you <<={...})
<<={resources.xml as resources}
<html>
  *{resources as r}{
    ...
  }
</html>

This method raises an ArgumentError if

  • no block is given or if the block is not of arity 1

  • any of the file extensions in exts is invalid

Raises:

  • (ArgumentError)


270
271
272
273
274
275
# File 'lib/wlang.rb', line 270

def self.data_loader(*exts, &block)
  raise(ArgumentError, "WLang::data_loader expects a block") unless block_given?
  raise(ArgumentError, "WLang::data_loader expects a block of arity 1") unless block.arity==1
  exts.each {|ext| check_file_extension(ext)   }
  exts.each {|ext| DATA_EXTENSIONS[ext] = block}
end

+ (Object) dialect(name, *extensions, &block)

Ensures, installs or query a dialect.

When name is a Dialect, returns it immediately. This helper is provided for methods that accept both qualified dialect name and dialect instance arguments. Calling WLang::dialect(arg) ensures that the result will be a Dialect instance in all cases (if the arg is valid).

Example:

# This methods does something with a wlang dialect. _dialect_ argument may
# be a Dialect instance or a qualified dialect name.
def my_method(dialect = 'wlang/active-string')
  # ensures the Dialect instance or raises an ArgumentError if the dialect
  # qualified name is invalid (returns nil otherwise !)
  dialect = WLang::dialect(dialect) 
end

When called with a block, this method installs a wlang dialect under name (which cannot be qualified). Extensions can be provided to let wlang automatically recognize files that are expressed in this dialect. The block is interpreted as code in the dialect DSL (domain specific language, see WLang::Dialect::DSL). Returns nil in this case.

Example:

# New dialect with 'my_dialect' qualified name and automatically installed
# to recognize '.wmyd' file extensions
WLang::dialect("my_dialect", '.wmyd') do
  # see WLang::Dialect::DSL for this part of the code
end

When called without a block this method returns a Dialect instance installed under name (which can be a qualified name). Extensions are ignored in this case. Returns nil if not found, a Dialect instance otherwise.

Example:

# Lookup for the 'wlang/xhtml' dialect
wxhtml = WLang::dialect('wlang/xhtml')

This method raises an ArgumentError if

  • name is not a valid dialect qualified name

  • any of the file extension in extensions is invalid



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/wlang.rb', line 159

def self.dialect(name, *extensions, &block)
  # first case, already a dialect
  return name if Dialect===name
  
  # other cases, argument validations
  check_qualified_dialect_name(name)
  extensions.each {|ext| check_file_extension(ext)}
  
  if block_given?
    # first case, dialect installation
    raise "Unsupported qualified names in dialect installation"\
      unless name.index('/').nil?
    Dialect::DSL.new(@dialect).dialect(name, *extensions, &block)
  else
    # second case, dialect lookup
    @dialect.dialect(name)
  end
end

+ (Object) dialect_tree

Returns the root of the dialect tree



78
79
80
# File 'lib/wlang.rb', line 78

def self.dialect_tree
  @dialect
end

+ (Object) encode(source, encoder_qname, options = {})

Shortcut for

WLang::encoder(encoder_qname).encode(source, options)

This method raises an ArgumentError

  • if source is not a String

  • if the encoder qualified name is invalid

It raises a WLang::Error if the encoder cannot be found

Raises:

  • (ArgumentError)


229
230
231
232
233
234
235
# File 'lib/wlang.rb', line 229

def self.encode(source, encoder_qname, options = {})
  raise ArgumentError, "String expected for source" unless String===source
  check_qualified_encoder_name(encoder_qname)
  encoder = WLang::encoder(encoder_qname)
  raise WLang::Error, "Unable to find encoder #{encoder_qname}" if encoder.nil?
  encoder.encode(source, options)
end

+ (Object) encoder(name)

Returns an encoder installed under a qualified name. Returns nil if not found. If name is already an Encoder instance, returns it immediately.

Example:

encoder = WLang::encoder('xhtml/entities-encoding')
encoder.encode('something that needs html entities escaping')

This method raises an ArgumentError if name is not a valid encoder qualified name.



213
214
215
216
# File 'lib/wlang.rb', line 213

def self.encoder(name)
  check_qualified_encoder_name(name)
  @dialect.encoder(name)
end

+ (Object) file_extension_map(extension, dialect_qname)

Maps a file extension to a dialect qualified name.

Example:

# We create an 'example' dialect
WLang::dialect('example') do
  # see WLang::dialect about creating a dialect
end

# We map .wex file extensions to our new dialect
WLang::file_extension_map('.wex', 'example')

This method raises an ArgumentError if the extension or dialect qualified name is not valid.



98
99
100
101
102
# File 'lib/wlang.rb', line 98

def self.file_extension_map(extension, dialect_qname)
  check_file_extension(extension)
  check_qualified_dialect_name(dialect_qname)
  WLang::FILE_EXTENSIONS[extension] = dialect_qname
end

+ (Object) file_instantiate(file, context = nil, dialect = nil, block_symbols = :braces)

Instantiates a file written in some wlang dialect, using a given context (providing instantiation data). If dialect is nil, tries to infer it from the file extension; otherwise dialect is expected to be a qualified dialect name or a Dialect instance. See instantiate about block_symbols.

Examples:

Wlang.file_instantiate "template.wtpl", {"who" => "Mr. Jones"}
Wlang.file_instantiate "template.xxx", {"who" => "Mr. Jones"}, "wlang/xhtml"

This method raises an ArgumentError if

  • file is not a readable file

  • context is not nil or a Hash

  • dialect is not a valid dialect qualified name, Dialect instance or nil

  • block_symbols is not in [:braces, :brackets, :parentheses]

It raises a WLang::Error

  • if no dialect can be infered from the file extension (if dialect was nil)

  • something goes wrong during instantiation (see WLang::Error and subclasses)

Raises:

  • (ArgumentError)


401
402
403
404
# File 'lib/wlang.rb', line 401

def self.file_instantiate(file, context = nil, dialect = nil, block_symbols = :braces)
  raise ArgumentError, "Hash expected for context argument" unless (context.nil? or Hash===context)
  file_template(file, dialect, block_symbols).instantiate(context || {}).to_s
end

+ (Object) file_template(file, dialect = nil, block_symbols = :braces)

Factors a template instance for a given file, optional dialect (if nil is passed, the dialect is infered from the extension) and block symbols (default to :braces)

Example:

# the file index.wtpl is a wlang source code in 'wlang/xhtml' dialect 
# (automatically infered from file extension)
template = WLang::template('index.wtpl')
puts template.instantiate(:who => 'world') # puts 'Hello world!'

This method raises an ArgumentError

  • if file does not exists, is not a file or is not readable

  • if dialect is not a valid qualified dialect name, Dialect instance, or nil

  • block_symbols is not in [:braces, :brackets, :parentheses]

It raises a WLang::Error

  • if no dialect can be infered from the file extension (if dialect was nil)

Raises:



339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/wlang.rb', line 339

def self.file_template(file, dialect = nil, block_symbols = :braces)
  check_readable_file(file)
  
  # Check the dialect
  dialect = self.infer_dialect(file) if dialect.nil?
  raise WLang::Error, "No known dialect for file extension '#{File.extname(file)}'\n"\
                    "Known extensions are: " << WLang::FILE_EXTENSIONS.keys.join(", ") if dialect.nil?
        
  # Build the template now
  template = template(File.read(file), dialect, block_symbols)
  template.source_file = file
  template
end

+ (Object) infer_dialect(uri)

Infers a dialect from a file extension. Returns nil if no dialect is currently mapped to the given extension (see file_extension_map)

This method never raises errors.



110
111
112
# File 'lib/wlang.rb', line 110

def self.infer_dialect(uri)
  WLang::FILE_EXTENSIONS[File.extname(uri)]
end

+ (Object) instantiate(source, context = {}, dialect = "wlang/active-string", block_symbols = :braces)

Instantiates a template written in some wlang dialect, using a given context (providing instantiation data). Returns instantiatiation as a String. If you want to instantiate the template in a specific buffer (a file or console for example), use Template. template is expected to be a String and context a Hash. To know available dialects, see WLang::StandardDialects. block_symbols can be :braces ('and '' pairs), :brackets ('[' and ']' pairs) or :parentheses ('(' and ')' pairs).

Examples:

WLang.instantiate "Hello ${who} !", {"who" => "Mr. Jones"}
WLang.instantiate "SELECT * FROM people WHERE name='{name}'", {"who" => "Mr. O'Neil"}, "wlang/sql"
WLang.instantiate "Hello $(who) !", {"who" => "Mr. Jones"}, "wlang/active-string", :parentheses

This method raises an ArgumentError if

  • source is not a String

  • context is not nil or a Hash

  • dialect is not a valid dialect qualified name or Dialect instance

  • block_symbols is not in [:braces, :brackets, :parentheses]

It raises a WLang::Error

  • something goes wrong during instantiation (see WLang::Error and subclasses)

Raises:

  • (ArgumentError)


376
377
378
379
# File 'lib/wlang.rb', line 376

def self.instantiate(source, context = {}, dialect="wlang/active-string", block_symbols = :braces)
  raise ArgumentError, "Hash expected for context argument" unless (context.nil? or Hash===context)
  template(source, dialect, block_symbols).instantiate(context || {}).to_s
end

+ (Object) load_data(uri, extension = nil)

Loads data from a given URI. If extension is omitted, tries to infer it from the uri, otherwise use it directly. Returns loaded data.

This method raises a WLang::Error if no data loader is installed for the found extension. It raises an ArgumentError if the file extension is invalid.

Raises:



284
285
286
287
288
289
# File 'lib/wlang.rb', line 284

def self.load_data(uri, extension=nil)
  check_file_extension(extension = extension.nil? ? File.extname(uri) : extension)
  loader = DATA_EXTENSIONS[extension]
  raise ::WLang::Error, "No data loader for #{extension}" if loader.nil?
  loader.call(uri) 
end

+ (Object) template(source, dialect = 'wlang/active-string', block_symbols = :braces)

Factors a template instance for a given string source, dialect (default to 'wlang/active-string') and block symbols (default to :braces)

Example:

# The template source code must be interpreted as wlang/xhtml
template = WLang::template('<p>Hello ${who}!</p>', 'wlang/xhtml')
str = template.instantiate(:hello => 'world')

# We may also use other block symbols...
template = WLang::template('<p>Hello $(who)!</p>', 'wlang/xhtml', :parentheses)
str = template.instantiate(:hello => 'world')

This method raises an ArgumentError if

  • source is not a String

  • dialect is not a valid dialect qualified name or Dialect instance

  • block_symbols is not in [:braces, :brackets, :parentheses]

Raises:

  • (ArgumentError)


312
313
314
315
316
317
# File 'lib/wlang.rb', line 312

def self.template(source, dialect = 'wlang/active-string', block_symbols = :braces)
  raise ArgumentError, "String expected for source" unless String===source
  raise ArgumentError, "Invalid symbols for block #{block_symbols}"\
    unless ::WLang::Template::BLOCK_SYMBOLS.keys.include?(block_symbols)
  template = Template.new(source, WLang::dialect(dialect), block_symbols)
end