class RubyLint::Definition::RubyObject
The RubyObject
class is the base definition class of ruby-lint. These so called definition classes are used for storing information about Ruby classes and instances. At their most basic form they are a mix between {RubyLint::Node} and a lookup table.
ruby-lint currently provides the following two definition classes:
-
{RubyLint::Definition::RubyObject}: the base definition class, used for most Ruby types and values.
-
{RubyLint::Definition::RubyMethod} definition class that is used for methods exclusively.
Using the RubyObject
class one could create a definition for the String
class as following:
string = RubyObject.new(:name => 'String', :type => :const) newline = RubyObject.new( :name => 'NEWLINE', :type => :const, :value => "\n" ) string.add(:const, newline.name, newline)
For more information see the documentation of the corresponding methods.
@!attribute [r] name
@return [String] The name of the object.
@!attribute [rw] value
@return [Mixed] The value of the object.
@!attribute [r] type
@return [Symbol] The type of object, e.g. `:const`.
@!attribute [r] definitions
@return [Hash{Symbol => Hash{String => Object}}] Hash keyed by type and name, containing all child the definitions.
@!attribute [rw] parents
@return [Array<RubyLint::Definition::RubyObject>] Array containing the parent definitions.
@!attribute [rw] reference_amount
@return [Numeric] The amount of times an object was referenced. Currently this is only used for variables.
@!attribute [rw] instance_type
@return [Symbol] Indicates if the object represents a class or an instance.
@!attribute [r] update_parents
@return [Array<Symbol>] A list of data types to also add to the parent definitions when adding an object to the current one.
@!attribute [r] members_as_value
@return [TrueClass|FalseClass] When set to `true` the {#value} getter returns a collection of the members instead of the manually defined value.
@!attribute [r] line
@return [Numeric] The line number of the definition.
@!attribute [r] column
@return [Numeric] The column number of the definition.
@!attribute [r] file
@return [String] The file path of the definition.
@!attribute [r] inherit_self
@return [TrueClass|FalseClass] When set to `false` child definitions created using `define_constant` do not inherit the current definition.
Constants
- LOOKUP_PARENT
Array containing items that should be looked up in the parent definition if they're not found in the current one.
@return [Array<Symbol>]
- PATH_SEPARATOR
String
used to separate segments in a constant path.@return [String]
- VALID_TYPES
Array containing the valid data types that can be stored.
@return [Array<Symbol>]
Attributes
Public Class Methods
Creates an object that represents an unknown value.
@return [RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 141 def self.create_unknown return new(:type => :unknown, :name => 'unknown') end
@example
string = RubyObject.new(:name => 'String', :type => :const)
@param [Hash] options Hash containing additional options such as the
parent definitions. For a list of available options see the corresponding getter/setter methods of this class.
@yieldparam [RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 155 def initialize(options = {}) @inherit_self = true options.each do |key, value| instance_variable_set("@#{key}", value) end @update_parents ||= [] @instance_type ||= :class @parents ||= [] @reference_amount ||= 0 @definitions = Hash.new { |hash, key| hash[key] = {} } @value = nil if members_as_value after_initialize if respond_to?(:after_initialize) yield self if block_given? end
Public Instance Methods
Adds a new definition to the definitions list.
@example
string = RubyObject.new(:name => 'String', :type => :const) newline = RubyObject.new( :name => 'NEWLINE', :type => :const, :value => "\n" ) string.add(newline.type, newline.name, newline)
@param [#to_sym] type The type of definition to add. @param [String] name The name of the definition. @param [RubyLint::Definition::RubyObject] value
@raise [TypeError] Raised when a value that is not a RubyObject
instance (or a subclass of this class) is given.
@raise [ArgumentError] Raised when the specified type was invalid.
# File lib/ruby-lint/definition/ruby_object.rb, line 227 def add(type, name, value) type = type.to_sym unless value.is_a?(RubyObject) raise TypeError, "Expected RubyObject but got #{value.class}" end unless VALID_TYPES.include?(type) raise ArgumentError, ":#{type} is not a valid type of data to add" end definitions[type][name] = value if update_parents.include?(type) update_parent_definitions(type, name, value) end end
Adds the definition object to the current one.
@see add
@param [RubyLint::Definition::RubyObject] definition
# File lib/ruby-lint/definition/ruby_object.rb, line 201 def add_definition(definition) add(definition.type, definition.name, definition) end
Returns the amount of definitions stored for a given type.
@param [#to_sym] type @return [Numeric]
# File lib/ruby-lint/definition/ruby_object.rb, line 461 def amount(type) return list(type).length end
Performs a method call on the current definition.
If the return value of a method definition is set to a Proc (or any other object that responds to `:call`) it will be called and passed the current instance as an argument.
TODO: add support for specifying method arguments.
@param [RubyLint::Definition::RubyObject] context The context in which
the method was called.
@return [Mixed]
# File lib/ruby-lint/definition/ruby_object.rb, line 366 def call(context = self) retval = respond_to?(:return_value) ? return_value : nil retval = retval.call(context) if retval.is_a?(Proc) return retval end
Mimics a method call by executing the method for the given name. This method should be defined in the current definition.
@param [String] name The name of the method. @return [Mixed]
# File lib/ruby-lint/definition/ruby_object.rb, line 334 def call_method(name) method = lookup(method_call_type, name) unless method raise NoMethodError, "Undefined method #{name} for #{self.inspect}" end return method.call(self) end
@return [TrueClass|FalseClass]
# File lib/ruby-lint/definition/ruby_object.rb, line 415 def class? return instance_type == :class end
@see {RubyLint::Definition::ConstantProxy#initialize} @param [String] name @param [RubyLint::Definition::Registry] registry @return [RubyLint::Definition::ConstantProxy]
# File lib/ruby-lint/definition/ruby_object.rb, line 641 def constant_proxy(name, registry = nil) return ConstantProxy.new(self, name, registry) end
Copies all the definitions in `source` of type `type` into the current definitions object.
@param [RubyLint::Definition::RubyObject] source @param [Symbol] source_type The type of definitions to copy from the
source.
@param [Symbol] target_type The type to store the definitions under,
set to the `source_type` value by default.
# File lib/ruby-lint/definition/ruby_object.rb, line 488 def copy(source, source_type, target_type = source_type) return unless source.definitions.key?(source_type) source.list(source_type).each do |definition| unless defines?(target_type, definition.name) add(target_type, definition.name, definition) end end end
Defines a new child constant.
@example
string.define_constant('NEWLINE')
@param [String] name @yieldparam [RubyLint::Definition::RubyObject] defs @return [RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 555 def define_constant(name, &block) if name.include?(PATH_SEPARATOR) path = name.split(PATH_SEPARATOR) target = lookup_constant_path(path[0..-2]) definition = target.define_constant(path[-1], &block) else definition = add_child_definition(:const, name, &block) end definition.define_self return definition end
Helper method that makes it easier to provide the two constructor methods `new` and `initialize`. The supplied block is yielded on both method definitions.
@example
some_object.define_constructors do |method| method.argument('name') end
# File lib/ruby-lint/definition/ruby_object.rb, line 621 def define_constructors(&block) define_method('new', &block) define_instance_method('initialize', &block) end
Defines a new global variable in the current definition.
@example
string.define_global_variable('$name', '...')
@param [String] name @param [Mixed] value
# File lib/ruby-lint/definition/ruby_object.rb, line 578 def define_global_variable(name, value = self.class.create_unknown) return add_child_definition(:gvar, name, value) end
Defines a new instance method.
@example
string.define_instance_method(:gsub)
@see RubyLint::Definition::RubyObject#define_method
@param [String,Symbol] name @yieldparam [RubyLint::Definition::RubyMethod] method @return [RubyLint::Definition::RubyMethod]
# File lib/ruby-lint/definition/ruby_object.rb, line 607 def define_instance_method(name, &block) return add_child_method(:instance_method, name, &block) end
Defines a new class method.
@example
string.define_method(:new)
@param [String,Symbol] name @yieldparam [RubyLint::Definition::RubyMethod] method @return [RubyLint::Definition::RubyMethod]
# File lib/ruby-lint/definition/ruby_object.rb, line 592 def define_method(name, &block) return add_child_method(:method, name, &block) end
Defines `self` on the current definition as both a class and instance method.
# File lib/ruby-lint/definition/ruby_object.rb, line 649 def define_self if instance? self_instance = self self_class = instance(:instance_type => :class) else self_instance = self.instance self_class = self end define_method('self') do |method| method.returns(self_class) end define_instance_method('self') do |method| method.returns(self_instance) end end
Checks if the specified definition is defined in the current object, ignoring data in any parent definitions.
@see RubyLint::Definition::RubyObject#has_definition?
@return [TrueClass|FalseClass]
# File lib/ruby-lint/definition/ruby_object.rb, line 433 def defines?(type, name) type, name = prepare_lookup(type, name) return definitions.key?(type) && definitions[type].key?(name) end
Returns `true` if the current definition list or one of the parents has the specified definition.
@example
string.has_definition?(:instance_method, 'downcase') # => true
@param [#to_sym] type @param [String] name @param [Array<RubyLint::Definition::RubyObject>] exclude
Parent definitions to exclude.
@return [TrueClass|FalseClass]
# File lib/ruby-lint/definition/ruby_object.rb, line 386 def has_definition?(type, name, exclude = []) type, name = prepare_lookup(type, name) if definitions.key?(type) and definitions[type].key?(name) return true elsif lookup_parent?(type) parents.each do |parent| next if exclude.include?(parent) return true if parent.has_definition?(type, name, exclude | [self]) end end return false end
Adds the object(s) to the list of parent definitions.
@param [Array<RubyLint::Definition::ConstantProxy>] definitions
# File lib/ruby-lint/definition/ruby_object.rb, line 631 def inherits(*definitions) self.parents.concat(definitions) end
Returns a pretty formatted String
that shows some info about the current definition.
@return [String]
# File lib/ruby-lint/definition/ruby_object.rb, line 673 def inspect attributes = [ %Q(@name="#{name}"), %Q(@type="#{type}"), %Q(@instance_type="#{instance_type}") ] # See <http://stackoverflow.com/a/2818916> for more info. address = (object_id << 1).to_s(16) return %Q(#<#{self.class}:0x#{address} #{attributes.join(' ')}>) end
Creates a new definition object based on the current one that represents an instance of a Ruby value (instead of a class).
@param [Hash] options Attributes to override in the new definition. @return [RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 505 def instance(options = {}) return shim(:instance_type => :instance) end
Changes the instance type of the current definition to `:instance`. If you want to return a new definition use {#instance} instead.
# File lib/ruby-lint/definition/ruby_object.rb, line 532 def instance! @instance_type = :instance end
@return [TrueClass|FalseClass]
# File lib/ruby-lint/definition/ruby_object.rb, line 422 def instance? return instance_type == :instance end
Returns a list of all the definitions for the specific type. This list excludes anything defined in parent definitions.
@example
string.list(:instance_method) # => [..., ..., ...]
@param [#to_sym] type @return [Array]
# File lib/ruby-lint/definition/ruby_object.rb, line 449 def list(type) type = type.to_sym return definitions.key?(type) ? definitions[type].values : [] end
Looks up a definition by the given type and name. If no data was found this method will try to look it up in any parent definitions.
If no definition was found `nil` will be returned.
@example
string = RubyObject.new(:name => 'String', :type => :const) newline = RubyObject.new( :name => 'NEWLINE', :type => :const, :value => "\n" ) string.add(newline.type, newline.name, newline) string.lookup(:const, 'NEWLINE') # => #<RubyLint::Definition...>
@param [#to_sym] type @param [String] name
@param [TrueClass|FalseClass] lookup_parent Whether definitions should
be looked up from parent definitions.
@param [Array<RubyLint::Definition::RubyObject>] exclude
A list of definitions to skip when looking up parents. This list is used to prevent stack errors when dealing with recursive definitions. A good example of this is `Logger` and `Logger::Severity` which both inherit from each other.
@return [RubyLint::Definition::RubyObject|NilClass]
# File lib/ruby-lint/definition/ruby_object.rb, line 277 def lookup(type, name, lookup_parent = true, exclude = []) type, name = prepare_lookup(type, name) found = nil if defines?(type, name) found = definitions[type][name] elsif type == :cbase found = top_scope # Look up the definition in the parent scope(s) (if any are set). This # takes the parents themselves also into account. elsif lookup_parent?(type) and lookup_parent parents.each do |parent| # If we've already processed the parent we'll skip it. next if exclude.include?(parent) parent_definition = determine_parent(parent, type, name, exclude) if parent_definition found = parent_definition break end end end return found end
Returns the definition for the given constant path.
@example
example.lookup_constant_path('A::B') # => #<RubyLint::Definition...>
@param [String|Array<String>] path @return [RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 314 def lookup_constant_path(path) constant = self path = path.split(PATH_SEPARATOR) if path.is_a?(String) path.each do |segment| found = constant.lookup(:const, segment) found ? constant = found : return end return constant end
Merges the definitions object `other` into the current one.
@param [RubyLint::Definition::RubyObject] other
# File lib/ruby-lint/definition/ruby_object.rb, line 470 def merge(other) other.definitions.each do |type, values| values.each do |name, definition| definitions[type][name] = definition end end end
Determines the call types for methods called on the current definition.
@return [Symbol]
# File lib/ruby-lint/definition/ruby_object.rb, line 408 def method_call_type return class? ? :method : :instance_method end
Returns `true` if a method is defined, similar to `respond_to?`.
@return [TrueClass|FalseClass]
# File lib/ruby-lint/definition/ruby_object.rb, line 349 def method_defined?(name) return has_definition?(method_call_type, name) end
Creates a new definition that inherits from the current one, acting as sort of a shim around the original one.
@param [Hash] options Attributes to set in the new definition. @return [RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 516 def shim(options = {}) options = { :name => name, :type => type, :instance_type => instance_type, :value => value, :parents => [self] }.merge(options) return self.class.new(options) end
# File lib/ruby-lint/definition/ruby_object.rb, line 686 def top_scope return self if type == :root scope = parents.last # the enclosing scope scope ? scope.top_scope : self end
Returns `true` if the object was referenced more than once.
@return [TrueClass|FalseClass]
# File lib/ruby-lint/definition/ruby_object.rb, line 541 def used? return reference_amount > 0 end
Returns the value of the definition. If `members_as_value` is set to `true` the return value is a Hash containing the names and values of each member.
@return [Hash|RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 182 def value return members_as_value ? list(:member) : @value end
Sets the value of the definition.
@param [Mixed] value
# File lib/ruby-lint/definition/ruby_object.rb, line 191 def value=(value) @value = value end
Private Instance Methods
Adds a new child definition to the current definition.
@param [Symbol] type The definition type. @param [String] name The name of the definition. @param [Mixed] value @return [RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 735 def add_child_definition(type, name, value = nil, &block) definition = self.class.new( :name => name, :type => type, :value => value, :parents => inherit_self ? [self] : nil, &block ) add(definition.type, definition.name, definition) return definition end
Adds a new child method to the current definition.
@see RubyLint::Definition::RubyObject#add_child_definition
# File lib/ruby-lint/definition/ruby_object.rb, line 754 def add_child_method(type, name, &block) definition = RubyMethod.new( :name => name, :type => type, :parents => [self], :instance_type => :instance, &block ) add(definition.type, definition.name, definition) return definition end
Determines what parent definition to use.
@param [RubyLint::Definition::RubyObject] parent @param [Symbol] type @param [String] name @param [Array<RubyLint::Definition::RubyObject>] exclude @return [RubyLint::Definition::RubyObject]
# File lib/ruby-lint/definition/ruby_object.rb, line 715 def determine_parent(parent, type, name, exclude = []) if parent.type == type and parent.name == name parent_definition = parent else exclude = exclude + [self] unless exclude.include?(self) parent_definition = parent.lookup(type, name, true, exclude) end return parent_definition end
Returns a boolean that indicates if the current definition type should be looked up in a parent definition.
@param [Symbol] type The type of definition. @return [Trueclass|FalseClass]
# File lib/ruby-lint/definition/ruby_object.rb, line 775 def lookup_parent?(type) return LOOKUP_PARENT.include?(type) && !parents.empty? end
Casts the type and name of data to look up to the correct values.
@param [#to_sym] type @param [#to_s] name @return [Array(Symbol,String)]
# File lib/ruby-lint/definition/ruby_object.rb, line 786 def prepare_lookup(type, name) return type.to_sym, name.to_s end
Updates each parent definition if it has an existing definition for hte given type and name.
@see add
# File lib/ruby-lint/definition/ruby_object.rb, line 700 def update_parent_definitions(type, name, value) parents.each do |parent| parent.add(type, name, value) if parent.has_definition?(type, name) end end