convio/watirmark

View on GitHub
lib/watirmark/models/factory.rb

Summary

Maintainability
B
4 hrs
Test Coverage
require_relative 'cucumber_helper'
require_relative 'default_values'
require_relative 'factory_methods'
require_relative 'factory_method_generators'
require_relative 'debug_methods'

module Watirmark
  module Model

    class Factory
      extend FactoryMethods

      include CucumberHelper
      include DebugMethods
      include FactoryMethodGenerators

      attr_accessor :defaults, :model_name, :models, :parent, :children, :model_type
      attr_reader   :keywords

      def marshal_dump
        [@keywords, @model_name, @models, @parent, @children, @model_type, self.to_h]
      end

      def marshal_load(obj)
        keyword_values = obj.pop
        @keywords, @model_name, @models, @parent, @children, @model_type = obj
        update keyword_values
      end

      def initialize(params={})
        @params = params
        @model_type = self.class.model_type_name
        @search     = self.class.search || Proc.new{nil}
        @keywords   = self.class.keys.dup || []
        @children   = self.class.children.dup.map(&:new)
        @updates    = []
        set_model_name
        set_default_values
        create_model_getters_and_setters
        set_initial_values
        Watirmark.logger.info inspect
      end

      def uuid
        @uuid ||= (Watirmark::Configuration.instance.uuid || UUID.new.generate(:compact)[0..9]).to_s
      end

      def hash_id(size = nil, type = :hex)
        size = size || Watirmark::Configuration.instance.hash_id_length || 8
        seed = Watirmark::Configuration.instance.hash_id_seed || "Watirmark Default Seed"
        @hash_id ||= generate_hash_id seed, size, type
      end

      def strip_equals_from_method_name(method)
        method.to_s.delete('=').to_sym
      end

      # The search_term, used for a controller's search can be defined in this model
      # or will look in a parent's model. This allows us to define it once for a composed model
      def search_term
        instance_eval(&@search) || (parent.search_term if parent)
      end


      def add_model(model)
        @children << model
        create_child_methods
        Watirmark.logger.info "Added Model #{model.inspect} to #{model_name || model_class_name}"
        return self
      end


      def find(model_class)
        return self if self.kind_of? model_class
        @children.each do |m|
          return m if m.model_type == model_class
          found_model = m.find model_class
          return found_model if found_model
        end
        nil
      end


      def inspect
        model_friendly_name = @model_name ? "#@model_name: " : nil
        model_details = " #{to_h}" unless to_h.empty?
        included_models = "\n   #{@children.map(&:inspect).join("\n   ")}" unless @children.empty?
        "#{model_friendly_name}#{model_class_name}#{model_details}#{included_models}"
      end


      def model_class_name
        name = self.class.inspect.to_s
        name = self.class.superclass.to_s if name.to_s =~ /Class/
        name = 'Model' if name.to_s =~ /Module/
        name.sub!(/.+::/,'')
        name
      end


      def includes? hash
        hash.each_pair { |key, value| return false unless send("#{key}") == value }
        true
      end


      # Update the model using the provided hash
      def update hash
        remove_empty_entries hash
        hash.each_pair { |key, value| send "#{key}=", value }
        self
      end
      alias :has :update

      # Update the model using the provided hash but only if exists (TODO: may not be needed any more)
      def update_existing_members hash
        remove_empty_entries hash
        hash.each_pair { |key, value| send "#{key}=", value if respond_to? "#{key}=".to_sym }
        self
      end

      def remove_empty_entries hash
        hash.delete_if {|k| k.nil? || k == ':' || k =~ /^\s+$/ || k.empty?}
      end

      def to_h
        h = {}
        @keywords.each do |key|
          begin
            name = key
            value = send(key)
            if value.kind_of?(Proc)
              h[name] = instance_eval(&value) unless value.nil?
            else
              h[name] = value unless value.nil?
            end
          rescue NoMethodError
            h[name] = "[defined at runtime]"
          end
        end
        h
      end

      def unique_instance_name
        class_name = self.class.name[/([^\:]+)Model$/i,1]
        model_name_exists = model_name.nil? ? false : (not model_name.empty?)
        unique_name = model_name_exists ? model_name : class_name.downcase
        unique_name_with_uuid = unique_name + "_" + uuid
      end

    private

      def set_model_name
        if @params[:model_name]
          @model_name = @params[:model_name]
          @params.delete(:model_name)
        end
      end

      def create_model_getters_and_setters
        create_keyword_methods
        create_child_methods
      end

      def set_default_values
        @defaults = self.class.default.dup
        @traits = self.class.included_traits || []
        include_defaults_from_traits @traits
      end

      def set_initial_values
        update @params
        add_debug_overrides
      end


      def include_defaults_from_traits(traits)
        traits.each do |trait_name|
          trait = Watirmark::Model::Traits.instance[trait_name]
          trait.defaults.each {|k, v| @defaults[k] = v unless @defaults.key?(k)}
          trait.traits.each {|t| include_defaults_from_traits([t])}
        end
      end

      def method_name model
        model.model_class_name.to_s.sub(/Model$/, '').underscore
      end

      def generate_hash_id(seed, size=8, type = :hex)
        seed_int = seed.scan(/./).inject(1) { |product, chr| product * chr.ord }
        prng     = Random.new(seed_int)
        if type == :alpha
          o = ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a
        else #:hex
          o = ('a'..'f').to_a + (0..9).to_a
        end
        (0...size.to_i).map { o.sample(random: prng) }.join
      end

    end
  end
end