mongomatic 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/mongomatic/base.rb +7 -0
- data/lib/mongomatic/m_hash.rb +149 -2
- data/lib/mongomatic/modifiers.rb +3 -2
- data/lib/mongomatic/type_converters.rb +146 -0
- data/lib/mongomatic/typed_fields.rb +58 -0
- data/lib/mongomatic.rb +2 -0
- data/test/helper.rb +16 -3
- data/test/test_typed_fields.rb +53 -0
- metadata +7 -3
    
        data/lib/mongomatic/base.rb
    CHANGED
    
    | @@ -3,6 +3,7 @@ module Mongomatic | |
| 3 3 | 
             
                include Mongomatic::Modifiers
         | 
| 4 4 | 
             
                include Mongomatic::Util
         | 
| 5 5 | 
             
                include Mongomatic::ActiveModelCompliancy
         | 
| 6 | 
            +
                include Mongomatic::TypedFields
         | 
| 6 7 |  | 
| 7 8 | 
             
                class << self
         | 
| 8 9 | 
             
                  # Returns this models own db attribute if set, otherwise will return Mongomatic.db
         | 
| @@ -108,6 +109,7 @@ module Mongomatic | |
| 108 109 | 
             
                end
         | 
| 109 110 |  | 
| 110 111 | 
             
                def valid?
         | 
| 112 | 
            +
                  check_typed_fields!
         | 
| 111 113 | 
             
                  self.errors = Mongomatic::Errors.new
         | 
| 112 114 | 
             
                  do_callback(:before_validate)
         | 
| 113 115 | 
             
                  validate
         | 
| @@ -136,6 +138,11 @@ module Mongomatic | |
| 136 138 | 
             
                  hash.has_key?(field)
         | 
| 137 139 | 
             
                end
         | 
| 138 140 |  | 
| 141 | 
            +
                def set_value_for_key(key, value)
         | 
| 142 | 
            +
                  field, hash = hash_for_field(key.to_s)
         | 
| 143 | 
            +
                  hash[field] = value
         | 
| 144 | 
            +
                end
         | 
| 145 | 
            +
                
         | 
| 139 146 | 
             
                def value_for_key(key)
         | 
| 140 147 | 
             
                  field, hash = hash_for_field(key.to_s, true)
         | 
| 141 148 | 
             
                  hash[field]
         | 
    
        data/lib/mongomatic/m_hash.rb
    CHANGED
    
    | @@ -1,4 +1,151 @@ | |
| 1 | 
            +
            require 'active_support/core_ext/hash/keys'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # This class has dubious semantics and we only have it so that
         | 
| 4 | 
            +
            # people can write params[:key] instead of params['key']
         | 
| 5 | 
            +
            # and they get the same value for both keys.
         | 
| 6 | 
            +
             | 
| 1 7 | 
             
            module Mongomatic
         | 
| 2 | 
            -
              class MHash <  | 
| 8 | 
            +
              class MHash < Hash
         | 
| 9 | 
            +
                def extractable_options?
         | 
| 10 | 
            +
                  true
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def initialize(constructor = {})
         | 
| 14 | 
            +
                  if constructor.is_a?(Hash)
         | 
| 15 | 
            +
                    super()
         | 
| 16 | 
            +
                    update(constructor)
         | 
| 17 | 
            +
                  else
         | 
| 18 | 
            +
                    super(constructor)
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def default(key = nil)
         | 
| 23 | 
            +
                  if key.is_a?(Symbol) && include?(key = key.to_s)
         | 
| 24 | 
            +
                    self[key]
         | 
| 25 | 
            +
                  else
         | 
| 26 | 
            +
                    super
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def self.new_from_hash_copying_default(hash)
         | 
| 31 | 
            +
                  Mongomatic::MHash.new(hash).tap do |new_hash|
         | 
| 32 | 
            +
                    new_hash.default = hash.default
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
         | 
| 37 | 
            +
                alias_method :regular_update, :update unless method_defined?(:regular_update)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                # Assigns a new value to the hash:
         | 
| 40 | 
            +
                #
         | 
| 41 | 
            +
                #   hash = MHash.new
         | 
| 42 | 
            +
                #   hash[:key] = "value"
         | 
| 43 | 
            +
                #
         | 
| 44 | 
            +
                def []=(key, value)
         | 
| 45 | 
            +
                  regular_writer(convert_key(key), convert_value(value))
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                # Updates the instantized hash with values from the second:
         | 
| 49 | 
            +
                #
         | 
| 50 | 
            +
                #   hash_1 = MHash.new
         | 
| 51 | 
            +
                #   hash_1[:key] = "value"
         | 
| 52 | 
            +
                #
         | 
| 53 | 
            +
                #   hash_2 = MHash.new
         | 
| 54 | 
            +
                #   hash_2[:key] = "New Value!"
         | 
| 55 | 
            +
                #
         | 
| 56 | 
            +
                #   hash_1.update(hash_2) # => {"key"=>"New Value!"}
         | 
| 57 | 
            +
                #
         | 
| 58 | 
            +
                def update(other_hash)
         | 
| 59 | 
            +
                  other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
         | 
| 60 | 
            +
                  self
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                alias_method :merge!, :update
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                # Checks the hash for a key matching the argument passed in:
         | 
| 66 | 
            +
                #
         | 
| 67 | 
            +
                #   hash = MHash.new
         | 
| 68 | 
            +
                #   hash["key"] = "value"
         | 
| 69 | 
            +
                #   hash.key? :key  # => true
         | 
| 70 | 
            +
                #   hash.key? "key" # => true
         | 
| 71 | 
            +
                #
         | 
| 72 | 
            +
                def key?(key)
         | 
| 73 | 
            +
                  super(convert_key(key))
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                alias_method :include?, :key?
         | 
| 77 | 
            +
                alias_method :has_key?, :key?
         | 
| 78 | 
            +
                alias_method :member?, :key?
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                # Fetches the value for the specified key, same as doing hash[key]
         | 
| 81 | 
            +
                def fetch(key, *extras)
         | 
| 82 | 
            +
                  super(convert_key(key), *extras)
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                # Returns an array of the values at the specified indices:
         | 
| 86 | 
            +
                #
         | 
| 87 | 
            +
                #   hash = MHash.new
         | 
| 88 | 
            +
                #   hash[:a] = "x"
         | 
| 89 | 
            +
                #   hash[:b] = "y"
         | 
| 90 | 
            +
                #   hash.values_at("a", "b") # => ["x", "y"]
         | 
| 91 | 
            +
                #
         | 
| 92 | 
            +
                def values_at(*indices)
         | 
| 93 | 
            +
                  indices.collect {|key| self[convert_key(key)]}
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                # Returns an exact copy of the hash.
         | 
| 97 | 
            +
                def dup
         | 
| 98 | 
            +
                  MHash.new(self)
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
         | 
| 102 | 
            +
                # Does not overwrite the existing hash.
         | 
| 103 | 
            +
                def merge(hash)
         | 
| 104 | 
            +
                  self.dup.update(hash)
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
         | 
| 108 | 
            +
                # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
         | 
| 109 | 
            +
                def reverse_merge(other_hash)
         | 
| 110 | 
            +
                  super self.class.new_from_hash_copying_default(other_hash)
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def reverse_merge!(other_hash)
         | 
| 114 | 
            +
                  replace(reverse_merge( other_hash ))
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                # Removes a specified key from the hash.
         | 
| 118 | 
            +
                def delete(key)
         | 
| 119 | 
            +
                  super(convert_key(key))
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def stringify_keys!; self end
         | 
| 123 | 
            +
                def stringify_keys; dup end
         | 
| 124 | 
            +
                undef :symbolize_keys!
         | 
| 125 | 
            +
                def symbolize_keys; to_hash.symbolize_keys end
         | 
| 126 | 
            +
                def to_options!; self end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                # Convert to a Hash with String keys.
         | 
| 129 | 
            +
                def to_hash
         | 
| 130 | 
            +
                  Hash.new(default).merge!(self)
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                protected
         | 
| 134 | 
            +
                  def convert_key(key)
         | 
| 135 | 
            +
                    key.kind_of?(Symbol) ? key.to_s : key
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  def convert_value(value)
         | 
| 139 | 
            +
                    case value
         | 
| 140 | 
            +
                    when Hash
         | 
| 141 | 
            +
                      self.class.new_from_hash_copying_default(value)
         | 
| 142 | 
            +
                    when Array
         | 
| 143 | 
            +
                      value.collect { |e| e.is_a?(Hash) ? self.class.new_from_hash_copying_default(e) : e }
         | 
| 144 | 
            +
                    else
         | 
| 145 | 
            +
                      value
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
                  end
         | 
| 3 148 | 
             
              end
         | 
| 4 | 
            -
            end
         | 
| 149 | 
            +
            end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
            MHash = Mongomatic::MHash
         | 
    
        data/lib/mongomatic/modifiers.rb
    CHANGED
    
    | @@ -150,7 +150,7 @@ module Mongomatic | |
| 150 150 | 
             
                #  user.set("name", "Ben")
         | 
| 151 151 | 
             
                def set(field, val, update_opts={}, safe=false)
         | 
| 152 152 | 
             
                  mongo_field = field.to_s
         | 
| 153 | 
            -
                  field, hash = hash_for_field(field.to_s)
         | 
| 153 | 
            +
                  #field, hash = hash_for_field(field.to_s)
         | 
| 154 154 |  | 
| 155 155 | 
             
                  op  = { "$set" => { mongo_field => val } }
         | 
| 156 156 | 
             
                  res = true
         | 
| @@ -158,7 +158,8 @@ module Mongomatic | |
| 158 158 | 
             
                  safe == true ? res = update!(update_opts, op) : update(update_opts, op)
         | 
| 159 159 |  | 
| 160 160 | 
             
                  if res
         | 
| 161 | 
            -
                     | 
| 161 | 
            +
                    set_value_for_key(field.to_s, val)
         | 
| 162 | 
            +
                    #hash[field] = val
         | 
| 162 163 | 
             
                    true
         | 
| 163 164 | 
             
                  end
         | 
| 164 165 | 
             
                end
         | 
| @@ -0,0 +1,146 @@ | |
| 1 | 
            +
            module Mongomatic
         | 
| 2 | 
            +
              module TypeConverters
         | 
| 3 | 
            +
                class CannotCastValue < RuntimeError; end
         | 
| 4 | 
            +
                
         | 
| 5 | 
            +
                def self.for_type(type)
         | 
| 6 | 
            +
                  eval "Mongomatic::TypeConverters::#{type.to_s.camelize}"
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
                
         | 
| 9 | 
            +
                class Base
         | 
| 10 | 
            +
                  def initialize(orig_val)
         | 
| 11 | 
            +
                    @orig_val = orig_val
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                  
         | 
| 14 | 
            +
                  def type_match?
         | 
| 15 | 
            +
                    raise "abstract"
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                  
         | 
| 18 | 
            +
                  def cast
         | 
| 19 | 
            +
                    if type_match?
         | 
| 20 | 
            +
                      @orig_val
         | 
| 21 | 
            +
                    else
         | 
| 22 | 
            +
                      convert_orig_val || raise(CannotCastValue)
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                  
         | 
| 26 | 
            +
                  def convert_orig_val
         | 
| 27 | 
            +
                    raise "abstract"
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
                
         | 
| 31 | 
            +
                class String < Base
         | 
| 32 | 
            +
                  def type_match?
         | 
| 33 | 
            +
                    @orig_val.class.to_s == "String"
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                  
         | 
| 36 | 
            +
                  def convert_orig_val
         | 
| 37 | 
            +
                    @orig_val.respond_to?(:to_s) ? @orig_val.to_s : nil
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
                
         | 
| 41 | 
            +
                class Float < Base
         | 
| 42 | 
            +
                  def type_match?
         | 
| 43 | 
            +
                    @orig_val.class.to_s == "Float"
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                  
         | 
| 46 | 
            +
                  def convert_orig_val
         | 
| 47 | 
            +
                    @orig_val.respond_to?(:to_f) ? @orig_val.to_f : nil
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
                
         | 
| 51 | 
            +
                class Fixnum < Base
         | 
| 52 | 
            +
                  def type_match?
         | 
| 53 | 
            +
                    @orig_val.class.to_s == "Fixnum"
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                  
         | 
| 56 | 
            +
                  def convert_orig_val
         | 
| 57 | 
            +
                    @orig_val.respond_to?(:to_i) ? @orig_val.to_i : nil
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
                
         | 
| 61 | 
            +
                class Array < Base
         | 
| 62 | 
            +
                  def type_match?
         | 
| 63 | 
            +
                    @orig_val.class.to_s == "Array"
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                  
         | 
| 66 | 
            +
                  def convert_orig_val
         | 
| 67 | 
            +
                    @orig_val.respond_to?(:to_a) ? @orig_val.to_a : nil
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                class Hash < Base
         | 
| 72 | 
            +
                  def type_match?
         | 
| 73 | 
            +
                    @orig_val.class.to_s == "Hash"
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                  
         | 
| 76 | 
            +
                  def convert_orig_val
         | 
| 77 | 
            +
                    [:to_h, :to_hash].each do |meth|
         | 
| 78 | 
            +
                      res = (@orig_val.respond_to?(meth) ? @orig_val.send(meth) : nil)
         | 
| 79 | 
            +
                      return res if !res.nil?
         | 
| 80 | 
            +
                    end; nil
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                class Bool < Base
         | 
| 85 | 
            +
                  def type_match?
         | 
| 86 | 
            +
                    @orig_val == true || @orig_val == false
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                  
         | 
| 89 | 
            +
                  def convert_orig_val
         | 
| 90 | 
            +
                    s_val = @orig_val.to_s.downcase
         | 
| 91 | 
            +
                    if %w(1 t true y yes).include?(s_val)
         | 
| 92 | 
            +
                      true
         | 
| 93 | 
            +
                    elsif %w(0 f false n no).include?(s_val)
         | 
| 94 | 
            +
                      false
         | 
| 95 | 
            +
                    else
         | 
| 96 | 
            +
                      nil
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
                    
         | 
| 101 | 
            +
                class Time < Base
         | 
| 102 | 
            +
                  def type_match?
         | 
| 103 | 
            +
                    @orig_val.class.to_s == "Time"
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                  def convert_orig_val
         | 
| 107 | 
            +
                    Time.parse(@orig_val.to_s)
         | 
| 108 | 
            +
                  rescue ArgumentError => e
         | 
| 109 | 
            +
                    nil
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
                
         | 
| 113 | 
            +
                class Regex < Base
         | 
| 114 | 
            +
                  def type_match?
         | 
| 115 | 
            +
                    @orig_val.class.to_s == "Regexp"
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
                  
         | 
| 118 | 
            +
                  def convert_orig_val
         | 
| 119 | 
            +
                    Regexp.new(@orig_val.to_s)
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
                
         | 
| 123 | 
            +
                class Symbol < Base
         | 
| 124 | 
            +
                  def type_match?
         | 
| 125 | 
            +
                    @orig_val.class.to_s == "Symbol"
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
                  
         | 
| 128 | 
            +
                  def convert_orig_val
         | 
| 129 | 
            +
                    @orig_val.respond_to?(:to_sym) ? @orig_val.to_sym : nil
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
                
         | 
| 133 | 
            +
                class ObjectId < Base
         | 
| 134 | 
            +
                  def type_match?
         | 
| 135 | 
            +
                    @orig_val.class.to_s == "BSON::ObjectId"
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
                  
         | 
| 138 | 
            +
                  def convert_orig_val
         | 
| 139 | 
            +
                    BSON::ObjectId(@orig_val.to_s)
         | 
| 140 | 
            +
                  rescue BSON::InvalidObjectId => e
         | 
| 141 | 
            +
                    nil
         | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
                
         | 
| 145 | 
            +
              end
         | 
| 146 | 
            +
            end
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            module Mongomatic
         | 
| 2 | 
            +
              # = Typed Fields
         | 
| 3 | 
            +
              # Explicitly specify the field types in your document. This is completely optional.
         | 
| 4 | 
            +
              # You can also set whether or not we should try to automatically cast a type to the
         | 
| 5 | 
            +
              # desired type.
         | 
| 6 | 
            +
              # = Examples
         | 
| 7 | 
            +
              #   typed_field "age",                :type => :fixnum,  :cast => true
         | 
| 8 | 
            +
              #   typed_field "manufacturer.name",  :type => :string,  :cast => false
         | 
| 9 | 
            +
              module TypedFields
         | 
| 10 | 
            +
                class InvalidType < RuntimeError; end
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                KNOWN_TYPES = [:string, :float, :fixnum, :array, :hash, :bool,
         | 
| 13 | 
            +
                               :time, :regex, :symbol, :object_id]
         | 
| 14 | 
            +
                
         | 
| 15 | 
            +
                def self.included(base)
         | 
| 16 | 
            +
                  base.send(:extend,  ClassMethods)
         | 
| 17 | 
            +
                  base.send(:include, InstanceMethods)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
                
         | 
| 20 | 
            +
                module ClassMethods
         | 
| 21 | 
            +
                  def typed_field(name, opts)
         | 
| 22 | 
            +
                    unless Mongomatic::TypedFields::KNOWN_TYPES.include?(opts[:type])
         | 
| 23 | 
            +
                      raise Mongomatic::TypedFields::Invalidtype, "#{opts[:type]}"
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                    
         | 
| 26 | 
            +
                    opts = {:cast => true}.merge(opts)
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    @typed_fields ||= {}
         | 
| 29 | 
            +
                    @typed_fields[name] = opts
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                  
         | 
| 32 | 
            +
                  def typed_fields
         | 
| 33 | 
            +
                    @typed_fields || {}
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end # ClassMethods
         | 
| 36 | 
            +
                
         | 
| 37 | 
            +
                module InstanceMethods
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def check_typed_fields!
         | 
| 40 | 
            +
                    self.class.typed_fields.each do |name, opts|
         | 
| 41 | 
            +
                      cast_or_raise_typed_field(name, opts)
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                  
         | 
| 45 | 
            +
                  def cast_or_raise_typed_field(name, opts)
         | 
| 46 | 
            +
                    val      = value_for_key(name.to_s); return if val.nil?
         | 
| 47 | 
            +
                    type     = opts[:type].to_sym
         | 
| 48 | 
            +
                    try_cast = opts[:cast]
         | 
| 49 | 
            +
                    
         | 
| 50 | 
            +
                    converter = Mongomatic::TypeConverters.for_type(type).new(val)
         | 
| 51 | 
            +
                    return true if converter.type_match?
         | 
| 52 | 
            +
                    raise(InvalidType, "#{name} should be a :#{type}") unless try_cast
         | 
| 53 | 
            +
                    set_value_for_key(name, converter.cast)
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                  
         | 
| 56 | 
            +
                end # InstanceMethods
         | 
| 57 | 
            +
              end # TypedFields
         | 
| 58 | 
            +
            end # Mongomatic
         | 
    
        data/lib/mongomatic.rb
    CHANGED
    
    | @@ -37,4 +37,6 @@ require "#{File.dirname(__FILE__)}/mongomatic/modifiers" | |
| 37 37 | 
             
            require "#{File.dirname(__FILE__)}/mongomatic/errors"
         | 
| 38 38 | 
             
            require "#{File.dirname(__FILE__)}/mongomatic/expectations"
         | 
| 39 39 | 
             
            require "#{File.dirname(__FILE__)}/mongomatic/active_model_compliancy"
         | 
| 40 | 
            +
            require "#{File.dirname(__FILE__)}/mongomatic/type_converters"
         | 
| 41 | 
            +
            require "#{File.dirname(__FILE__)}/mongomatic/typed_fields"
         | 
| 40 42 | 
             
            require "#{File.dirname(__FILE__)}/mongomatic/base"
         | 
    
        data/test/helper.rb
    CHANGED
    
    | @@ -2,9 +2,10 @@ require 'rubygems' | |
| 2 2 | 
             
            gem 'minitest', "~> 2.0"
         | 
| 3 3 | 
             
            require 'pp'
         | 
| 4 4 |  | 
| 5 | 
            -
            $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 6 | 
            -
            $LOAD_PATH.unshift(File.dirname(__FILE__))
         | 
| 7 | 
            -
            require  | 
| 5 | 
            +
            # $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 6 | 
            +
            # $LOAD_PATH.unshift(File.dirname(__FILE__))
         | 
| 7 | 
            +
            require "#{File.dirname(__FILE__)}/../lib/mongomatic"
         | 
| 8 | 
            +
            #require 'mongomatic'
         | 
| 8 9 |  | 
| 9 10 | 
             
            Mongomatic.db = Mongo::Connection.new.db("mongomatic_test")
         | 
| 10 11 |  | 
| @@ -103,3 +104,15 @@ class Foobar < Mongomatic::Base | |
| 103 104 | 
             
                errors << "missing style" if self["style"].blank?
         | 
| 104 105 | 
             
              end
         | 
| 105 106 | 
             
            end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            class Rig < Mongomatic::Base
         | 
| 109 | 
            +
              # :cast => true, :raise => false is the default
         | 
| 110 | 
            +
              typed_field "age",                :type => :fixnum,  :cast => true
         | 
| 111 | 
            +
              typed_field "manufacturer.name",  :type => :string,  :cast => true
         | 
| 112 | 
            +
              typed_field "manufacturer.phone", :type => :string,  :cast => false
         | 
| 113 | 
            +
              typed_field "waist_measurement",  :type => :float,   :cast => true
         | 
| 114 | 
            +
              typed_field "friends_rig_id",     :type => :object_id, :cast => true
         | 
| 115 | 
            +
            end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
             | 
| 118 | 
            +
             | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            require 'helper'
         | 
| 2 | 
            +
            require 'minitest/autorun'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class TestTypedFields < MiniTest::Unit::TestCase
         | 
| 5 | 
            +
              def setup
         | 
| 6 | 
            +
                Rig.collection.drop
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def test_raising_error_on_invalid_type
         | 
| 10 | 
            +
                r = Rig.new
         | 
| 11 | 
            +
                assert r["manufacturer"].blank?
         | 
| 12 | 
            +
                r["manufacturer"] = { "phone" => 123 }
         | 
| 13 | 
            +
                assert_raises(Mongomatic::TypedFields::InvalidType) { r.valid? }
         | 
| 14 | 
            +
                r["manufacturer"] = {}
         | 
| 15 | 
            +
                r["manufacturer"]["phone"] = "(800) 123 456 789"
         | 
| 16 | 
            +
                assert_equal true, r.valid?
         | 
| 17 | 
            +
                assert_equal "(800) 123 456 789", r["manufacturer"]["phone"]
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
              
         | 
| 20 | 
            +
              def test_cast_string
         | 
| 21 | 
            +
                r = Rig.new
         | 
| 22 | 
            +
                r["manufacturer"] = {}
         | 
| 23 | 
            +
                r["manufacturer"]["name"] = ["Wings","Parachuting","Company"]
         | 
| 24 | 
            +
                assert_equal ["Wings","Parachuting","Company"], r["manufacturer"]["name"]
         | 
| 25 | 
            +
                assert r.valid?
         | 
| 26 | 
            +
                assert_equal ["Wings","Parachuting","Company"].to_s, r["manufacturer"]["name"]
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
              
         | 
| 29 | 
            +
              def test_cast_number
         | 
| 30 | 
            +
                r = Rig.new
         | 
| 31 | 
            +
                r["age"] = "4"
         | 
| 32 | 
            +
                assert_equal "4", r["age"]
         | 
| 33 | 
            +
                assert r.valid?
         | 
| 34 | 
            +
                assert_equal 4, r["age"]
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
              
         | 
| 37 | 
            +
              def test_cast_float
         | 
| 38 | 
            +
                r = Rig.new
         | 
| 39 | 
            +
                r["waist_measurement"] = "34.3"
         | 
| 40 | 
            +
                assert_equal "34.3", r["waist_measurement"]
         | 
| 41 | 
            +
                assert r.valid?
         | 
| 42 | 
            +
                assert_equal 34.3, r["waist_measurement"]
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
              
         | 
| 45 | 
            +
              def test_cast_object_id
         | 
| 46 | 
            +
                r = Rig.new
         | 
| 47 | 
            +
                assert r.insert
         | 
| 48 | 
            +
                r2 = Rig.new
         | 
| 49 | 
            +
                r2["friends_rig_id"] = r["_id"].to_s
         | 
| 50 | 
            +
                r2.insert
         | 
| 51 | 
            +
                assert_equal "BSON::ObjectId", r2["friends_rig_id"].class.to_s
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version | |
| 5 5 | 
             
              segments: 
         | 
| 6 6 | 
             
              - 0
         | 
| 7 7 | 
             
              - 6
         | 
| 8 | 
            -
              -  | 
| 9 | 
            -
              version: 0.6. | 
| 8 | 
            +
              - 4
         | 
| 9 | 
            +
              version: 0.6.4
         | 
| 10 10 | 
             
            platform: ruby
         | 
| 11 11 | 
             
            authors: 
         | 
| 12 12 | 
             
            - Ben Myles
         | 
| @@ -14,7 +14,7 @@ autorequire: | |
| 14 14 | 
             
            bindir: bin
         | 
| 15 15 | 
             
            cert_chain: []
         | 
| 16 16 |  | 
| 17 | 
            -
            date: 2010-11- | 
| 17 | 
            +
            date: 2010-11-16 00:00:00 -08:00
         | 
| 18 18 | 
             
            default_executable: 
         | 
| 19 19 | 
             
            dependencies: 
         | 
| 20 20 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -99,6 +99,8 @@ files: | |
| 99 99 | 
             
            - lib/mongomatic/expectations/present.rb
         | 
| 100 100 | 
             
            - lib/mongomatic/m_hash.rb
         | 
| 101 101 | 
             
            - lib/mongomatic/modifiers.rb
         | 
| 102 | 
            +
            - lib/mongomatic/type_converters.rb
         | 
| 103 | 
            +
            - lib/mongomatic/typed_fields.rb
         | 
| 102 104 | 
             
            - lib/mongomatic/util.rb
         | 
| 103 105 | 
             
            - LICENSE
         | 
| 104 106 | 
             
            - README.rdoc
         | 
| @@ -108,6 +110,7 @@ files: | |
| 108 110 | 
             
            - test/test_misc.rb
         | 
| 109 111 | 
             
            - test/test_modifiers.rb
         | 
| 110 112 | 
             
            - test/test_persistence.rb
         | 
| 113 | 
            +
            - test/test_typed_fields.rb
         | 
| 111 114 | 
             
            - test/test_validations.rb
         | 
| 112 115 | 
             
            has_rdoc: true
         | 
| 113 116 | 
             
            homepage: http://mongomatic.com/
         | 
| @@ -148,4 +151,5 @@ test_files: | |
| 148 151 | 
             
            - test/test_misc.rb
         | 
| 149 152 | 
             
            - test/test_modifiers.rb
         | 
| 150 153 | 
             
            - test/test_persistence.rb
         | 
| 154 | 
            +
            - test/test_typed_fields.rb
         | 
| 151 155 | 
             
            - test/test_validations.rb
         |