reactrb/reactive-record

View on GitHub
lib/reactive_record/active_record/class_methods.rb

Summary

Maintainability
A
1 hr
Test Coverage
module ActiveRecord

  module ClassMethods

    def base_class

      unless self < Base
        raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
      end

      if superclass == Base || superclass.abstract_class?
        self
      else
        superclass.base_class
      end

    end

    def abstract_class?
      defined?(@abstract_class) && @abstract_class == true
    end

    def primary_key
      base_class.instance_eval { @primary_key_value || :id }
    end

    def primary_key=(val)
     base_class.instance_eval {  @primary_key_value = val }
    end

    def inheritance_column
      base_class.instance_eval {@inheritance_column_value || "type"}
    end

    def inheritance_column=(name)
      base_class.instance_eval {@inheritance_column_value = name}
    end

    def model_name
      # in reality should return ActiveModel::Name object, blah blah
      name
    end

    def find(id)
      base_class.instance_eval {ReactiveRecord::Base.find(self, primary_key, id)}
    end

    def find_by(opts = {})
      base_class.instance_eval {ReactiveRecord::Base.find(self, opts.first.first, opts.first.last)}
    end

    def enum(*args)
      # when we implement schema validation we should also implement value checking
    end

    def method_missing(name, *args, &block)
      if args.count == 1 && name =~ /^find_by_/ && !block
        find_by(name.gsub(/^find_by_/, "") => args[0])
      else
        raise "#{self.name}.#{name}(#{args}) (called class method missing)"
      end
    end

    def abstract_class=(val)
      @abstract_class = val
    end

    def scope(name, body)
      singleton_class.send(:define_method, name) do | *args |
        args = (args.count == 0) ? name : [name, *args]
        ReactiveRecord::Base.class_scopes(self)[args] ||= ReactiveRecord::Collection.new(self, nil, nil, self, args)
      end
      singleton_class.send(:define_method, "#{name}=") do |collection|
        ReactiveRecord::Base.class_scopes(self)[name] = collection
      end
    end

    def all
      ReactiveRecord::Base.class_scopes(self)[:all] ||= ReactiveRecord::Collection.new(self, nil, nil, self, "all")
    end

    def all=(collection)
      ReactiveRecord::Base.class_scopes(self)[:all] = collection
    end

    # def server_methods(*methods)
    #   methods.each do |method|
    #     define_method(method) do |*args|
    #       if args.count == 0
    #         @backing_record.reactive_get!(method, :initialize)
    #       else
    #         @backing_record.reactive_get!([[method]+args], :initialize)
    #       end
    #     end
    #     define_method("#{method}!") do |*args|
    #       if args.count == 0
    #         @backing_record.reactive_get!(method, :force)
    #       else
    #         @backing_record.reactive_get!([[method]+args], :force)
    #       end
    #     end
    #   end
    # end
    #
    # alias_method :server_method, :server_methods

    [:belongs_to, :has_many, :has_one].each do |macro|
      define_method(macro) do |*args| # is this a bug in opal?  saying name, scope=nil, opts={} does not work!
        name = args.first
        opts = (args.count > 1 and args.last.is_a? Hash) ? args.last : {}
        Associations::AssociationReflection.new(self, macro, name, opts)
      end
    end

    def composed_of(name, opts = {})
      Aggregations::AggregationReflection.new(base_class, :composed_of, name, opts)
    end

    def column_names
      []  # it would be great to figure out how to get this information on the client!  For now we just return an empty array
    end

    [
      "table_name=", "before_validation", "with_options", "validates_presence_of", "validates_format_of",
      "accepts_nested_attributes_for", "before_create", "after_create", "before_save", "after_save", "before_destroy", "where", "validate",
      "attr_protected", "validates_numericality_of", "default_scope", "has_attached_file", "attr_accessible",
      "serialize"
    ].each do |method|
      define_method(method.to_s) { |*args, &block| }
    end

    def _react_param_conversion(param, opt = nil)
      # defines how react will convert incoming json to this ActiveRecord model
      #TIMING times = {start: Time.now.to_f, json_start: 0, json_end: 0, db_load_start: 0, db_load_end: 0}
      #TIMING times[:json_start] = Time.now.to_f
      param = Native(param)
      param = JSON.from_object(param.to_n) if param.is_a? Native::Object
      #TIMING times[:json_end] = Time.now.to_f
      result = if param.is_a? self
        param
      elsif param.is_a? Hash
        if opt == :validate_only
          klass = ReactiveRecord::Base.infer_type_from_hash(self, param)
          klass == self or klass < self
        else
          if param[primary_key]
            target = find(param[primary_key])
          else
            target = new
          end
          #TIMING times[:db_load_start] = Time.now.to_f
          ReactiveRecord::Base.load_from_json(Hash[param.collect { |key, value| [key, [value]] }], target)
          #TIMING times[:db_load_end] = Time.now.to_f
          target
        end
      else
        nil
      end
      #TIMING times[:end] = Time.now.to_f
      #TIMING puts "times - total: #{'%.04f' % (times[:end]-times[:start])}, native conversion: #{'%.04f' % (times[:json_end]-times[:json_start])}, loading: #{'%.04f' % (times[:db_load_end]-times[:db_load_start])}"
      result
    end

  end

end