alsemyonov/yard-mongoid

View on GitHub
lib/yard/handlers/ruby/mongoid/field_handler.rb

Summary

Maintainability
A
1 hr
Test Coverage
require 'yard'
require 'yard/handlers/ruby/mongoid/extensions'
require 'yard/handlers/ruby/mongoid/helpers'

module YARD::Handlers::Ruby
  module Mongoid
    class FieldHandler < YARD::Handlers::Ruby::AttributeHandler
      include Extensions
      include YARD::Handlers::Ruby::Mongoid::Helpers

      MONGOID_FIELDS = 'Fields'.freeze

      namespace_only

      handles method_call(:field)

      def process
        name = statement.parameters[0].last

        if name.type == :symbol
          name = name.source[1..-1]

          add_field_getter(effected_namespace, name, class_name, default_value)
          add_field_setter(effected_namespace, name, class_name)
          # register_field_presence(effected_namespace, name, scope)
          # register_field_change(effected_namespace, name, scope)
          # register_field_changed(effected_namespace, name, scope)
          # register_field_was(effected_namespace, name, scope)
          # register_field_reset(effected_namespace, name, scope)
        end
      end

      protected

      def class_name
        return @class_name if instance_variable_defined?(:@class_name)

        if statement.parameters.size > 2
          statement.parameters[1].source.split(/,\s*/).each do |key_and_value|
            if key_and_value =~ hash_args[:type]
              @class_name = Regexp.last_match(2)
              break
            end
          end
          @class_name = 'true, false' if @class_name == 'Boolean'
        end
        @class_name ||= 'String'
      end

      def default_value
        return @default_value if instance_variable_defined?(:@default_value)

        if statement.parameters.size > 2
          statement.parameters[1].source.split(/,\s*/).each do |key_and_value|
            if key_and_value =~ /(:default\s*=>|default:)\s*(.+)\s*$/
              @default_value = Regexp.last_match(2)
              break
            end
          end
        end
        @default_value ||= nil
      end

      protected

      def hash_args
        @hash_args ||= Hash.new do |hash, key|
          hash[key] = /(:#{key.to_s}\s*=>|#{key.to_s}:\s)\s*([A-Z][A-Za-z0-9]*)/
        end
      end

      # @yieldparam [YARD::CodeObjects::MethodObject]
      def add_field_method(namespace, name)
        register YARD::CodeObjects::MethodObject.new(namespace, name, :instance) do |mo|
          mo.group = MONGOID_FIELDS
          mo.visibility ||= :public
          mo.source = statement.source
          mo.dynamic = true
          mo.signature = "def #{name}"
          mo.docstring = '' if mo.docstring.empty?
          yield(mo) if block_given?
        end
      end

      # Creates and registers a new +name+ method in +namespace+
      # with an instance or class +scope+
      # @param [NamespaceObject] namespace the namespace
      # @param [String, Symbol] name the method name
      def add_field_getter(namespace, name, type = 'Object', default_value = nil)
        add_field_method(namespace, name) do |o|
          default_value = "(defaults to: +#{default_value}+) " if default_value
          o.docstring.add_tag get_tag(:return, default_value, type)
        end
      end

      # Creates and registers a new +name+= method in +namespace+
      # with an instance or class +scope+
      # @param [NamespaceObject] namespace the namespace
      # @param [String, Symbol] name the method name
      def add_field_setter(namespace, name, type = 'Object')
        add_field_method(namespace, "#{name}=") do |o|
          o.parameters = [[name, nil]]
          o.docstring.add_tag get_tag(:return, nil, type)
          o.docstring.add_tag get_tag(:param, "new #{name}", type, name)
        end
      end

      # Creates and registers a new +name+? method in +namespace+
      # with an instance or class +scope+
      # @param [NamespaceObject] namespace the namespace
      # @param [String, Symbol] name the method name
      def add_field_presence(namespace, name)
        add_field_method(namespace, "#{name}?")
      end

      # Creates and registers a new +name+_change method in +namespace+
      # with an instance or class +scope+
      # @param [NamespaceObject] namespace the namespace
      # @param [String, Symbol] name the method name
      def add_field_change(namespace, name)
        add_field_method(namespace, "#{name}_change")
      end

      # Creates and registers a new +name+_changed? method in +namespace+
      # with an instance or class +scope+
      # @param [NamespaceObject] namespace the namespace
      # @param [String, Symbol] name the method name
      def add_field_changed(namespace, name)
        add_field_method(namespace, "#{name}_changed?")
      end

      # Creates and registers a new +name+_was method in +namespace+
      # with an instance or class +scope+
      # @param [NamespaceObject] namespace the namespace
      # @param [String, Symbol] name the method name
      def register_field_was(namespace, name)
        add_field_method(namespace, "#{name}_was")
      end

      # Creates and registers a new reset_+name+! method in +namespace+
      # with an instance or class +scope+
      # @param [NamespaceObject] namespace the namespace
      # @param [String, Symbol] name the method name
      def register_field_reset(namespace, name)
        add_field_method(namespace, "reset_#{name}!")
      end
    end
  end
end