lib/yard/verifier.rb in yard-0.9.18 vs lib/yard/verifier.rb in yard-0.9.19

- old
+ new

@@ -1,151 +1,151 @@ -# frozen_string_literal: true -module YARD - # Similar to a Proc, but runs a set of Ruby expressions using a small - # DSL to make tag lookups easier. - # - # The syntax is as follows: - # * All syntax is Ruby compatible - # * +object+ (+o+ for short) exist to access the object being verified - # * +@TAGNAME+ is translated into +object.tag('TAGNAME')+ - # * +@@TAGNAME+ is translated into +object.tags('TAGNAME')+ - # * +object+ can be omitted as target for method calls (it is implied) - # - # @example Create a verifier to check for objects that don't have @private tags - # verifier = Verifier.new('!@private') - # verifier.call(object) # => true (no @private tag) - # @example Create a verifier to find any return tag with an empty description - # Verifier.new('@return.text.empty?') - # # Equivalent to: - # Verifier.new('object.tag(:return).text.empty?') - # @example Check if there are any @param tags - # Verifier.new('@@param.empty?') - # # Equivalent to: - # Verifier.new('object.tags(:param).empty?') - # @example Using +object+ or +o+ to look up object attributes directly - # Verifier.new('object.docstring == "hello world"') - # # Equivalent to: - # Verifier.new('o.docstring == "hello world"') - # @example Without using +object+ or +o+ - # Verifier.new('tag(:return).size == 1 || has_tag?(:author)') - # @example Specifying multiple expressions - # Verifier.new('@return', '@param', '@yield') - # # Equivalent to: - # Verifier.new('@return && @param && @yield') - class Verifier - # @return [Array<String>] a list of all expressions the verifier checks for - # @since 0.5.6 - attr_reader :expressions - - def expressions=(value) - @expressions = value - create_method_from_expressions - end - - # Creates a verifier from a set of expressions - # - # @param [Array<String>] expressions a list of Ruby expressions to - # parse. - def initialize(*expressions) - @expressions = [] - add_expressions(*expressions) - end - - # Adds a set of expressions and recompiles the verifier - # - # @param [Array<String>] expressions a list of expressions - # @return [void] - # @since 0.5.6 - def add_expressions(*expressions) - self.expressions += expressions.flatten - end - - # Passes any method calls to the object from the {#call} - def method_missing(sym, *args, &block) - if object.respond_to?(sym) - object.send(sym, *args, &block) - else - super - end - end - - # Tests the expressions on the object. - # - # @note If the object is a {CodeObjects::Proxy} the result will always be true. - # @param [CodeObjects::Base] object the object to verify - # @return [Boolean] the result of the expressions - def call(object) - return true if object.is_a?(CodeObjects::Proxy) - modify_nilclass - @object = object - retval = __execute ? true : false - unmodify_nilclass - retval - end - - # Runs a list of objects against the verifier and returns the subset - # of verified objects. - # - # @param [Array<CodeObjects::Base>] list a list of code objects - # @return [Array<CodeObjects::Base>] a list of code objects that match - # the verifier. - def run(list) - list.reject {|item| call(item).is_a?(FalseClass) } - end - - protected - - # @return [CodeObjects::Base] the current object being tested - attr_reader :object - alias o object - - private - - # @private - NILCLASS_METHODS = [:type, :method_missing] - - # Modifies nil to not throw NoMethodErrors. This allows - # syntax like object.tag(:return).text to work if the #tag - # call returns nil, which means users don't need to perform - # stringent nil checking - # - # @return [void] - def modify_nilclass - NILCLASS_METHODS.each do |meth| - NilClass.send(:define_method, meth) {|*args| } - end - end - - # Returns the state of NilClass back to normal - # @return [void] - def unmodify_nilclass - NILCLASS_METHODS.each do |meth| - next unless nil.respond_to?(meth) - NilClass.send(:remove_method, meth) - end - end - - # Creates the +__execute+ method by evaluating the expressions - # as Ruby code - # @return [void] - def create_method_from_expressions - expr = expressions.map {|e| "(#{parse_expression(e)})" }.join(" && ") - - instance_eval(<<-eof, __FILE__, __LINE__ + 1) - begin; undef __execute; rescue NameError; end - def __execute; #{expr}; end - eof - end - - # Parses a single expression, handling some of the DSL syntax. - # - # The syntax "@tag" should be turned into object.tag(:tag), - # and "@@tag" should be turned into object.tags(:tag) - # - # @return [String] the parsed expression - def parse_expression(expr) - expr = expr.gsub(/@@(?:(\w+)|\{([\w\.]+)\})/, 'object.tags("\1\2")') - expr = expr.gsub(/@(?:(\w+)|\{([\w\.]+)\})/, 'object.tag("\1\2")') - expr - end - end -end +# frozen_string_literal: true +module YARD + # Similar to a Proc, but runs a set of Ruby expressions using a small + # DSL to make tag lookups easier. + # + # The syntax is as follows: + # * All syntax is Ruby compatible + # * +object+ (+o+ for short) exist to access the object being verified + # * +@TAGNAME+ is translated into +object.tag('TAGNAME')+ + # * +@@TAGNAME+ is translated into +object.tags('TAGNAME')+ + # * +object+ can be omitted as target for method calls (it is implied) + # + # @example Create a verifier to check for objects that don't have @private tags + # verifier = Verifier.new('!@private') + # verifier.call(object) # => true (no @private tag) + # @example Create a verifier to find any return tag with an empty description + # Verifier.new('@return.text.empty?') + # # Equivalent to: + # Verifier.new('object.tag(:return).text.empty?') + # @example Check if there are any @param tags + # Verifier.new('@@param.empty?') + # # Equivalent to: + # Verifier.new('object.tags(:param).empty?') + # @example Using +object+ or +o+ to look up object attributes directly + # Verifier.new('object.docstring == "hello world"') + # # Equivalent to: + # Verifier.new('o.docstring == "hello world"') + # @example Without using +object+ or +o+ + # Verifier.new('tag(:return).size == 1 || has_tag?(:author)') + # @example Specifying multiple expressions + # Verifier.new('@return', '@param', '@yield') + # # Equivalent to: + # Verifier.new('@return && @param && @yield') + class Verifier + # @return [Array<String>] a list of all expressions the verifier checks for + # @since 0.5.6 + attr_reader :expressions + + def expressions=(value) + @expressions = value + create_method_from_expressions + end + + # Creates a verifier from a set of expressions + # + # @param [Array<String>] expressions a list of Ruby expressions to + # parse. + def initialize(*expressions) + @expressions = [] + add_expressions(*expressions) + end + + # Adds a set of expressions and recompiles the verifier + # + # @param [Array<String>] expressions a list of expressions + # @return [void] + # @since 0.5.6 + def add_expressions(*expressions) + self.expressions += expressions.flatten + end + + # Passes any method calls to the object from the {#call} + def method_missing(sym, *args, &block) + if object.respond_to?(sym) + object.send(sym, *args, &block) + else + super + end + end + + # Tests the expressions on the object. + # + # @note If the object is a {CodeObjects::Proxy} the result will always be true. + # @param [CodeObjects::Base] object the object to verify + # @return [Boolean] the result of the expressions + def call(object) + return true if object.is_a?(CodeObjects::Proxy) + modify_nilclass + @object = object + retval = __execute ? true : false + unmodify_nilclass + retval + end + + # Runs a list of objects against the verifier and returns the subset + # of verified objects. + # + # @param [Array<CodeObjects::Base>] list a list of code objects + # @return [Array<CodeObjects::Base>] a list of code objects that match + # the verifier. + def run(list) + list.reject {|item| call(item).is_a?(FalseClass) } + end + + protected + + # @return [CodeObjects::Base] the current object being tested + attr_reader :object + alias o object + + private + + # @private + NILCLASS_METHODS = [:type, :method_missing] + + # Modifies nil to not throw NoMethodErrors. This allows + # syntax like object.tag(:return).text to work if the #tag + # call returns nil, which means users don't need to perform + # stringent nil checking + # + # @return [void] + def modify_nilclass + NILCLASS_METHODS.each do |meth| + NilClass.send(:define_method, meth) {|*args| } + end + end + + # Returns the state of NilClass back to normal + # @return [void] + def unmodify_nilclass + NILCLASS_METHODS.each do |meth| + next unless nil.respond_to?(meth) + NilClass.send(:remove_method, meth) + end + end + + # Creates the +__execute+ method by evaluating the expressions + # as Ruby code + # @return [void] + def create_method_from_expressions + expr = expressions.map {|e| "(#{parse_expression(e)})" }.join(" && ") + + instance_eval(<<-eof, __FILE__, __LINE__ + 1) + begin; undef __execute; rescue NameError; end + def __execute; #{expr}; end + eof + end + + # Parses a single expression, handling some of the DSL syntax. + # + # The syntax "@tag" should be turned into object.tag(:tag), + # and "@@tag" should be turned into object.tags(:tag) + # + # @return [String] the parsed expression + def parse_expression(expr) + expr = expr.gsub(/@@(?:(\w+)|\{([\w\.]+)\})/, 'object.tags("\1\2")') + expr = expr.gsub(/@(?:(\w+)|\{([\w\.]+)\})/, 'object.tag("\1\2")') + expr + end + end +end