module MetaRuby::DSLs
DSLs-related tools
Find through method missing¶ ↑
The find through method missing functionality is meant to allow classes to turn objects that can be found (with a method that finds an object by its name) into an attribute call as e.g.
task.test_event # => task.find_event("test")
See {DSLs::FindThroughMethodMissing} for a complete description
Documentation parsing¶ ↑
This provides the logic to find a documentation block above a DSL-like object creation. For instance, given a class that looks like
class Task def event(name) # creates an event object with the given name end end
Used in a DSL context like so:
# The test event allows us # # To provide an example event 'test'
The parse_documentation method allows to extract the comment block above the 'event' call. See {DSLs.parse_documentation} for more information
Public Class Methods
Generic implementation to create suffixed accessors for child objects on a class
Given an object category (let's say 'state'), this allows to properly implement a method-missing based accessor of the style
blabla_state
using a find_state method that the object should respond to
@param [Object] object the object on which the find method is going to
be called
@param [Symbol] m the method name @param [Array] args the method arguments @param [Array<String>] suffixes the accessor suffixes that should be
resolved. The last argument can be a hash, in which case the keys are used as suffixes and the values are the name of the find methods that should be used.
@return [Object,nil] an object if one of the listed suffixes matches
the method name, or nil if the method name does not match the requested pattern.
@raise [NoMethodError] if the requested object does not exist (i.e. if
the find method returns nil)
@raise [ArgumentError] if the method name matches one of the suffixes,
but arguments were given. It is raised regardless of the existence of the requested object
@example
class MyClass def find_state(name) states[name] end def find_transition(name) transitions[name] end def method_missing(m, *args, &block) MetaRuby::DSLs.find_through_method_missing(self, m, args, 'state', 'transition') || super end end object = MyClass.new object.add_state 'my' object.my_state # will resolve the 'my' state
# File lib/metaruby/dsls/find_through_method_missing.rb, line 108 def self.find_through_method_missing(object, m, args, suffix_match) return false if m == :to_ary m = m.to_s suffix_match.each do |s, find_method_name| if m.end_with?(s) name = m[0, m.size - s.size] if !args.empty? raise ArgumentError, "expected zero arguments to #{m}, got #{args.size}", caller(4) else return object.send(find_method_name, name) end end end nil end
# File lib/metaruby/dsls/find_through_method_missing.rb, line 125 def self.has_through_method_missing?(object, m, suffix_match) return false if m == :to_ary m = m.to_s suffix_match.each do |s, has_method_name| if m.end_with?(s) name = m[0, m.size - s.size] return !!object.send(has_method_name, name) end end false end
Looks for the documentation block for the element that is being built.
@param [#===] file_match an object (typically a regular expression)
that matches the file name in which the DSL is being used
@param [#===] trigger_method an object (typically a regular expression)
that matches the name of the method that initiates the creation of the element whose documentation we are looking for.
@return [String,nil] the parsed documentation, or nil if there is no
documentation
@example find the documentation block of an event creation
# assuming the following toplevel DSL code in a file called test.orogen task "Task" do # Just an example event event "test" end # One would use the following code to extract the documentation # above the test event declaration. The call must be made within the # event creation code MetaRuby::DSLs.parse_documentation_block(/test\.orogen$/, "event")
# File lib/metaruby/dsls/doc.rb, line 26 def self.parse_documentation_block(file_match, trigger_method = /.*/) last_method_matched = false caller_locations(1).each do |call| this_method_matched = if trigger_method === call.label true elsif call.label == 'method_missing' last_method_matched else false end if !this_method_matched && last_method_matched && (file_match === call.absolute_path) if File.file?(call.absolute_path) return parse_documentation_block_at(call.absolute_path, call.lineno) else return end end last_method_matched = this_method_matched end nil end
Parses upwards a Ruby documentation block whose last line starts at or just before the given line in the given file
@param [String] file @param [Integer] line @return [String,nil] the parsed documentation, or nil if there is no
documentation
# File lib/metaruby/dsls/doc.rb, line 56 def self.parse_documentation_block_at(file, line) lines = File.readlines(file) block = [] # Lines are given 1-based (as all editors work that way), and we # want the line before the definition. Remove two line = line - 2 space_count = nil while true l = lines[line] comment_match = /^\s*#/.match(l) if comment_match comment_line = comment_match.post_match.rstrip stripped_line = comment_line.lstrip leading_spaces = comment_line.size - stripped_line.size if !stripped_line.empty? && (!space_count || space_count > leading_spaces) space_count = leading_spaces end block.unshift(comment_line) else break end line = line - 1 end if !block.empty? space_count ||= 0 block.map { |l| l[space_count..-1] }.join("\n") end end