ampere 0.1.3 → 1.0.0
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/.rvmrc +1 -1
- data/README.md +26 -6
- data/VERSION +1 -1
- data/ampere.gemspec +3 -3
- data/lib/ampere/model.rb +189 -176
- data/spec/models/indices_spec.rb +17 -7
- data/spec/models/model_spec.rb +9 -3
- data/spec/models/queries_spec.rb +3 -1
- data/spec/models/relationships/belongs_to_spec.rb +9 -3
- data/spec/models/relationships/has_many_spec.rb +6 -2
- data/spec/models/relationships/has_one_spec.rb +6 -2
- data/spec/models/updates_spec.rb +3 -1
- data/spec/module/collections_spec.rb +3 -1
- metadata +20 -20
    
        data/.rvmrc
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            rvm use 1.9. | 
| 1 | 
            +
            rvm use 1.9.3@ampere
         | 
    
        data/README.md
    CHANGED
    
    | @@ -5,12 +5,24 @@ Ampere is an ActiveRecord-style ORM for the Redis key/value data store. | |
| 5 5 | 
             
            This is under active development right now and not very far along. Stay
         | 
| 6 6 | 
             
            tuned for further developments.
         | 
| 7 7 |  | 
| 8 | 
            +
            ## A note about version 1.0 (IMPORTANT!!!)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            For the 1.0 release I changed Ampere's API so that instead of subclassing
         | 
| 11 | 
            +
            `Ampere::Model` to use Ampere's methods, you include it as a mixin. This
         | 
| 12 | 
            +
            change has been reflected in the examples below.
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            This change was to unify the usage of Ampere a little more with usage of
         | 
| 15 | 
            +
            Mongoid, and also so that users of Ampere can use their own class hierarchies,
         | 
| 16 | 
            +
            which at some later date might have significance with how Ampere works.
         | 
| 17 | 
            +
             | 
| 8 18 | 
             
            ## Usage
         | 
| 9 19 |  | 
| 10 20 | 
             
            Write a model class and make it inherit from the `Ampere::Model` class.
         | 
| 11 21 | 
             
            These work pretty similarly to how they do in ActiveRecord or Mongoid.
         | 
| 12 22 |  | 
| 13 | 
            -
                class Car | 
| 23 | 
            +
                class Car
         | 
| 24 | 
            +
                  include Ampere::Model
         | 
| 25 | 
            +
                  
         | 
| 14 26 | 
             
                  field :make
         | 
| 15 27 | 
             
                  field :model
         | 
| 16 28 | 
             
                  field :year
         | 
| @@ -19,7 +31,9 @@ These work pretty similarly to how they do in ActiveRecord or Mongoid. | |
| 19 31 | 
             
                  has_many :passengers
         | 
| 20 32 | 
             
                end
         | 
| 21 33 |  | 
| 22 | 
            -
                class Engine | 
| 34 | 
            +
                class Engine
         | 
| 35 | 
            +
                  include Ampere::Model
         | 
| 36 | 
            +
                  
         | 
| 23 37 | 
             
                  field :displacement
         | 
| 24 38 | 
             
                  field :cylinders
         | 
| 25 39 | 
             
                  field :configuration
         | 
| @@ -27,7 +41,9 @@ These work pretty similarly to how they do in ActiveRecord or Mongoid. | |
| 27 41 | 
             
                  belongs_to :car
         | 
| 28 42 | 
             
                end
         | 
| 29 43 |  | 
| 30 | 
            -
                class Passenger | 
| 44 | 
            +
                class Passenger
         | 
| 45 | 
            +
                  include Ampere::Model
         | 
| 46 | 
            +
                  
         | 
| 31 47 | 
             
                  field :name
         | 
| 32 48 | 
             
                  field :seat
         | 
| 33 49 |  | 
| @@ -46,9 +62,11 @@ be slower if one of the keys you are searching by isn't indexed). | |
| 46 62 |  | 
| 47 63 | 
             
            ### Indexes
         | 
| 48 64 |  | 
| 49 | 
            -
            Indexes work similar to Mongoid. They are non-unique.
         | 
| 65 | 
            +
            Indexes work similar to Mongoid. They are non-unique by default.
         | 
| 50 66 |  | 
| 51 | 
            -
                class Student | 
| 67 | 
            +
                class Student
         | 
| 68 | 
            +
                  include Ampere::Model
         | 
| 69 | 
            +
                  
         | 
| 52 70 | 
             
                  field :last_name
         | 
| 53 71 | 
             
                  field :first_name
         | 
| 54 72 |  | 
| @@ -60,7 +78,9 @@ last_name will happen faster. | |
| 60 78 |  | 
| 61 79 | 
             
            You can also define indices on multiple fields.
         | 
| 62 80 |  | 
| 63 | 
            -
                class Student | 
| 81 | 
            +
                class Student
         | 
| 82 | 
            +
                  include Ampere::Model
         | 
| 83 | 
            +
                  
         | 
| 64 84 | 
             
                  field :last_name
         | 
| 65 85 | 
             
                  field :first_name
         | 
| 66 86 |  | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0. | 
| 1 | 
            +
            1.0.0
         | 
    
        data/ampere.gemspec
    CHANGED
    
    | @@ -5,11 +5,11 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |s|
         | 
| 7 7 | 
             
              s.name = "ampere"
         | 
| 8 | 
            -
              s.version = "0. | 
| 8 | 
            +
              s.version = "1.0.0"
         | 
| 9 9 |  | 
| 10 10 | 
             
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         | 
| 11 11 | 
             
              s.authors = ["Max Thom Stahl"]
         | 
| 12 | 
            -
              s.date = "2012- | 
| 12 | 
            +
              s.date = "2012-04-20"
         | 
| 13 13 | 
             
              s.description = "An ActiveRecord/Mongoid-esque object model for the Redis key/value data store."
         | 
| 14 14 | 
             
              s.email = "max@villainousindustri.es"
         | 
| 15 15 | 
             
              s.extra_rdoc_files = [
         | 
| @@ -49,7 +49,7 @@ Gem::Specification.new do |s| | |
| 49 49 | 
             
              s.homepage = "http://github.com/mstahl/ampere"
         | 
| 50 50 | 
             
              s.licenses = ["EPL"]
         | 
| 51 51 | 
             
              s.require_paths = ["lib"]
         | 
| 52 | 
            -
              s.rubygems_version = "1.8. | 
| 52 | 
            +
              s.rubygems_version = "1.8.17"
         | 
| 53 53 | 
             
              s.summary = "A pure Ruby ORM for Redis."
         | 
| 54 54 |  | 
| 55 55 | 
             
              if s.respond_to? :specification_version then
         | 
    
        data/lib/ampere/model.rb
    CHANGED
    
    | @@ -1,11 +1,23 @@ | |
| 1 1 | 
             
            module Ampere
         | 
| 2 | 
            -
               | 
| 3 | 
            -
                attr_reader :id
         | 
| 2 | 
            +
              module Model
         | 
| 4 3 |  | 
| 5 | 
            -
                 | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 4 | 
            +
                def self.included(base)
         | 
| 5 | 
            +
                  base.extend(ClassMethods)
         | 
| 6 | 
            +
                  
         | 
| 7 | 
            +
                  base.class_eval do
         | 
| 8 | 
            +
                    attr_reader :id
         | 
| 9 | 
            +
                  
         | 
| 10 | 
            +
                    attr_accessor :fields
         | 
| 11 | 
            +
                    attr_accessor :field_defaults
         | 
| 12 | 
            +
                    attr_accessor :indices
         | 
| 13 | 
            +
                    attr_accessor :field_types
         | 
| 14 | 
            +
                
         | 
| 15 | 
            +
                    @fields         = []
         | 
| 16 | 
            +
                    @field_defaults = {}
         | 
| 17 | 
            +
                    @indices        = []
         | 
| 18 | 
            +
                    @field_types    = {}
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 9 21 |  | 
| 10 22 | 
             
                ### Instance methods
         | 
| 11 23 |  | 
| @@ -138,225 +150,226 @@ module Ampere | |
| 138 150 |  | 
| 139 151 | 
             
                ### Class methods
         | 
| 140 152 |  | 
| 141 | 
            -
                 | 
| 142 | 
            -
             | 
| 143 | 
            -
                   | 
| 144 | 
            -
             | 
| 153 | 
            +
                module ClassMethods
         | 
| 154 | 
            +
                  # Returns an array of all the records that have been stored.
         | 
| 155 | 
            +
                  def all
         | 
| 156 | 
            +
                    Ampere.connection.keys("#{to_s.downcase}.*").map{|m| find m}
         | 
| 157 | 
            +
                  end
         | 
| 145 158 |  | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 159 | 
            +
                  # Declares a belongs_to relationship to another model.
         | 
| 160 | 
            +
                  def belongs_to(field_name, options = {})
         | 
| 161 | 
            +
                    has_one field_name, options
         | 
| 162 | 
            +
                  end
         | 
| 150 163 |  | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 164 | 
            +
                  # Like @indices, but only returns the compound indices this class defines.
         | 
| 165 | 
            +
                  def compound_indices
         | 
| 166 | 
            +
                    @indices.select{|i| i.class == Array}
         | 
| 167 | 
            +
                  end
         | 
| 155 168 |  | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 169 | 
            +
                  # Returns the number of instances of this record that have been stored.
         | 
| 170 | 
            +
                  def count
         | 
| 171 | 
            +
                    Ampere.connection.keys("#{to_s.downcase}.*").length
         | 
| 172 | 
            +
                  end
         | 
| 160 173 |  | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 174 | 
            +
                  # Instantiates and saves a new record.
         | 
| 175 | 
            +
                  def create(hash = {})
         | 
| 176 | 
            +
                    new(hash).save
         | 
| 177 | 
            +
                  end
         | 
| 165 178 |  | 
| 166 | 
            -
             | 
| 167 | 
            -
             | 
| 168 | 
            -
             | 
| 169 | 
            -
             | 
| 179 | 
            +
                  # Deletes the record with the given ID.
         | 
| 180 | 
            +
                  def delete(id)
         | 
| 181 | 
            +
                    Ampere.connection.del(id)
         | 
| 182 | 
            +
                  end
         | 
| 170 183 |  | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 184 | 
            +
                  # Declares a field. See the README for more details.
         | 
| 185 | 
            +
                  def field(name, options = {})
         | 
| 186 | 
            +
                    @fields         ||= []
         | 
| 187 | 
            +
                    @field_defaults ||= {}
         | 
| 188 | 
            +
                    @indices        ||= []
         | 
| 189 | 
            +
                    @unique_indices ||= []
         | 
| 190 | 
            +
                    @field_types    ||= {}
         | 
| 178 191 |  | 
| 179 | 
            -
             | 
| 192 | 
            +
                    @fields << name
         | 
| 180 193 |  | 
| 181 | 
            -
             | 
| 194 | 
            +
                    # attr_accessor :"#{name}"
         | 
| 182 195 |  | 
| 183 | 
            -
             | 
| 184 | 
            -
             | 
| 196 | 
            +
                    # Handle default value
         | 
| 197 | 
            +
                    @field_defaults[name] = options[:default]
         | 
| 185 198 |  | 
| 186 | 
            -
             | 
| 187 | 
            -
             | 
| 188 | 
            -
             | 
| 189 | 
            -
             | 
| 199 | 
            +
                    # Handle type, if any
         | 
| 200 | 
            +
                    if options[:type] then
         | 
| 201 | 
            +
                      @field_types[:"#{name}"] = options[:type].to_s
         | 
| 202 | 
            +
                    end
         | 
| 190 203 |  | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 193 | 
            -
             | 
| 204 | 
            +
                    define_method :"#{name}" do
         | 
| 205 | 
            +
                      instance_variable_get("@#{name}") or self.class.field_defaults[name]
         | 
| 206 | 
            +
                    end
         | 
| 194 207 |  | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
             | 
| 198 | 
            -
             | 
| 199 | 
            -
             | 
| 208 | 
            +
                    define_method :"#{name}=" do |val|
         | 
| 209 | 
            +
                      if not self.class.field_types[:"#{name}"] or val.is_a?(eval(self.class.field_types[:"#{name}"])) then
         | 
| 210 | 
            +
                        instance_variable_set("@#{name}", val)
         | 
| 211 | 
            +
                      else
         | 
| 212 | 
            +
                        raise "Cannot set field of type #{self.class.field_types[name.to_sym]} with #{val.class} value"
         | 
| 213 | 
            +
                      end
         | 
| 200 214 | 
             
                    end
         | 
| 201 215 | 
             
                  end
         | 
| 202 | 
            -
                end
         | 
| 203 216 |  | 
| 204 | 
            -
             | 
| 205 | 
            -
             | 
| 206 | 
            -
             | 
| 217 | 
            +
                  def fields
         | 
| 218 | 
            +
                    @fields
         | 
| 219 | 
            +
                  end
         | 
| 207 220 |  | 
| 208 | 
            -
             | 
| 209 | 
            -
             | 
| 210 | 
            -
             | 
| 221 | 
            +
                  def field_defaults
         | 
| 222 | 
            +
                    @field_defaults
         | 
| 223 | 
            +
                  end
         | 
| 211 224 |  | 
| 212 | 
            -
             | 
| 213 | 
            -
             | 
| 214 | 
            -
             | 
| 225 | 
            +
                  def field_types
         | 
| 226 | 
            +
                    @field_types
         | 
| 227 | 
            +
                  end
         | 
| 215 228 |  | 
| 216 | 
            -
             | 
| 217 | 
            -
             | 
| 218 | 
            -
             | 
| 219 | 
            -
             | 
| 220 | 
            -
             | 
| 229 | 
            +
                  # Finds the record with the given ID, or the first that matches the given conditions
         | 
| 230 | 
            +
                  def find(options = {})
         | 
| 231 | 
            +
                    if options.class == String then
         | 
| 232 | 
            +
                      if Ampere.connection.exists(options) then
         | 
| 233 | 
            +
                        new(Ampere.connection.hgetall(options), true)
         | 
| 234 | 
            +
                      else
         | 
| 235 | 
            +
                        nil
         | 
| 236 | 
            +
                      end
         | 
| 221 237 | 
             
                    else
         | 
| 222 | 
            -
                       | 
| 238 | 
            +
                      # TODO Write a handler for this case, even if it's an exception
         | 
| 239 | 
            +
                      raise "Cannot find by #{options.class} yet"
         | 
| 223 240 | 
             
                    end
         | 
| 224 | 
            -
                  else
         | 
| 225 | 
            -
                    # TODO Write a handler for this case, even if it's an exception
         | 
| 226 | 
            -
                    raise "Cannot find by #{options.class} yet"
         | 
| 227 241 | 
             
                  end
         | 
| 228 | 
            -
                end
         | 
| 229 242 |  | 
| 230 | 
            -
             | 
| 231 | 
            -
             | 
| 232 | 
            -
             | 
| 233 | 
            -
             | 
| 243 | 
            +
                  # Defines a has_one relationship with another model. See the README for more details.
         | 
| 244 | 
            +
                  def has_one(field_name, options = {})
         | 
| 245 | 
            +
                    referred_klass_name = (options[:class] or options['class'] or field_name)
         | 
| 246 | 
            +
                    my_klass_name = to_s.downcase
         | 
| 234 247 |  | 
| 235 | 
            -
             | 
| 248 | 
            +
                    field :"#{field_name}_id"
         | 
| 236 249 |  | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 239 | 
            -
             | 
| 240 | 
            -
             | 
| 250 | 
            +
                    define_method(field_name.to_sym) do
         | 
| 251 | 
            +
                      return if self.send("#{field_name}_id").nil?
         | 
| 252 | 
            +
                      eval(referred_klass_name.to_s.capitalize).find(self.send("#{field_name}_id"))
         | 
| 253 | 
            +
                    end
         | 
| 241 254 |  | 
| 242 | 
            -
             | 
| 243 | 
            -
             | 
| 244 | 
            -
             | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 255 | 
            +
                    define_method(:"#{field_name}=") do |val|
         | 
| 256 | 
            +
                      return nil if val.nil?
         | 
| 257 | 
            +
                      # Set attr with key where referred model is stored
         | 
| 258 | 
            +
                      self.send("#{field_name}_id=", val.id)
         | 
| 259 | 
            +
                      # Also update that model's hash with a pointer back to here
         | 
| 260 | 
            +
                      val.send("#{my_klass_name}_id=", self.send("id"))
         | 
| 261 | 
            +
                    end
         | 
| 248 262 | 
             
                  end
         | 
| 249 | 
            -
                end
         | 
| 250 263 |  | 
| 251 | 
            -
             | 
| 252 | 
            -
             | 
| 253 | 
            -
             | 
| 254 | 
            -
             | 
| 264 | 
            +
                  # Defines a has_many relationship with another model. See the README for more details.
         | 
| 265 | 
            +
                  def has_many(field_name, options = {})
         | 
| 266 | 
            +
                    klass_name = (options[:class] or options['class'] or field_name.to_s.gsub(/s$/, ''))
         | 
| 267 | 
            +
                    my_klass_name = to_s.downcase
         | 
| 255 268 |  | 
| 256 | 
            -
             | 
| 257 | 
            -
             | 
| 258 | 
            -
             | 
| 269 | 
            +
                    define_method(:"#{field_name}") do
         | 
| 270 | 
            +
                      (Ampere.connection.smembers("#{to_s.downcase}.#{self.id}.has_many.#{field_name}")).map do |id|
         | 
| 271 | 
            +
                        eval(klass_name.to_s.capitalize).find(id)
         | 
| 272 | 
            +
                      end
         | 
| 259 273 | 
             
                    end
         | 
| 260 | 
            -
                  end
         | 
| 261 274 |  | 
| 262 | 
            -
             | 
| 263 | 
            -
             | 
| 264 | 
            -
             | 
| 265 | 
            -
             | 
| 266 | 
            -
             | 
| 275 | 
            +
                    define_method(:"#{field_name}=") do |val|
         | 
| 276 | 
            +
                      val.each do |v|
         | 
| 277 | 
            +
                        Ampere.connection.sadd("#{to_s.downcase}.#{self.id}.has_many.#{field_name}", v.id)
         | 
| 278 | 
            +
                        # Set pointer for belongs_to
         | 
| 279 | 
            +
                        Ampere.connection.hset(v.id, "#{my_klass_name}_id", self.send("id"))
         | 
| 280 | 
            +
                      end
         | 
| 267 281 | 
             
                    end
         | 
| 268 282 | 
             
                  end
         | 
| 269 | 
            -
                end
         | 
| 270 283 |  | 
| 271 | 
            -
             | 
| 272 | 
            -
             | 
| 273 | 
            -
             | 
| 274 | 
            -
             | 
| 275 | 
            -
             | 
| 276 | 
            -
             | 
| 277 | 
            -
             | 
| 278 | 
            -
             | 
| 284 | 
            +
                  # Defines an index. See the README for more details.
         | 
| 285 | 
            +
                  def index(field_name, options = {})
         | 
| 286 | 
            +
                    # TODO There has just got to be a better way to handle this.
         | 
| 287 | 
            +
                    @fields         ||= []
         | 
| 288 | 
            +
                    @field_defaults ||= {}
         | 
| 289 | 
            +
                    @indices        ||= []
         | 
| 290 | 
            +
                    @unique_indices ||= []
         | 
| 291 | 
            +
                    @field_types    ||= {}
         | 
| 279 292 |  | 
| 280 | 
            -
             | 
| 281 | 
            -
             | 
| 282 | 
            -
             | 
| 283 | 
            -
             | 
| 284 | 
            -
             | 
| 285 | 
            -
             | 
| 286 | 
            -
             | 
| 287 | 
            -
             | 
| 288 | 
            -
             | 
| 289 | 
            -
             | 
| 293 | 
            +
                    if field_name.class == String or field_name.class == Symbol then
         | 
| 294 | 
            +
                      # Singular index
         | 
| 295 | 
            +
                      raise "Can't index a nonexistent field!" unless @fields.include?(field_name)
         | 
| 296 | 
            +
                    elsif field_name.class == Array then
         | 
| 297 | 
            +
                      # Compound index
         | 
| 298 | 
            +
                      field_name.each{|f| raise "Can't index a nonexistent field!" unless @fields.include?(f)}
         | 
| 299 | 
            +
                      field_name.sort!
         | 
| 300 | 
            +
                    else
         | 
| 301 | 
            +
                      raise "Can't index a #{field_name.class}"
         | 
| 302 | 
            +
                    end
         | 
| 290 303 |  | 
| 291 | 
            -
             | 
| 292 | 
            -
             | 
| 293 | 
            -
             | 
| 304 | 
            +
                    @indices << field_name
         | 
| 305 | 
            +
                    @unique_indices << field_name if options[:unique]
         | 
| 306 | 
            +
                  end
         | 
| 294 307 |  | 
| 295 | 
            -
             | 
| 296 | 
            -
             | 
| 297 | 
            -
             | 
| 308 | 
            +
                  def indices
         | 
| 309 | 
            +
                    @indices
         | 
| 310 | 
            +
                  end
         | 
| 298 311 |  | 
| 299 | 
            -
             | 
| 300 | 
            -
             | 
| 301 | 
            -
             | 
| 312 | 
            +
                  def unique_indices
         | 
| 313 | 
            +
                    @unique_indices
         | 
| 314 | 
            +
                  end
         | 
| 302 315 |  | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 306 | 
            -
             | 
| 307 | 
            -
             | 
| 308 | 
            -
             | 
| 309 | 
            -
             | 
| 310 | 
            -
             | 
| 316 | 
            +
                  # Finds an array of records which match the given conditions. This method is
         | 
| 317 | 
            +
                  # much faster when all the fields given are indexed.
         | 
| 318 | 
            +
                  def where(options = {})
         | 
| 319 | 
            +
                    if options.empty? then
         | 
| 320 | 
            +
                      Ampere::Collection.new(eval(to_s), [])
         | 
| 321 | 
            +
                    else
         | 
| 322 | 
            +
                      indexed_fields    = (options.keys & @indices) + compound_indices_for(options)
         | 
| 323 | 
            +
                      nonindexed_fields = (options.keys - @indices) - compound_indices_for(options).flatten
         | 
| 311 324 |  | 
| 312 | 
            -
             | 
| 325 | 
            +
                      results = nil
         | 
| 313 326 |  | 
| 314 | 
            -
             | 
| 315 | 
            -
             | 
| 316 | 
            -
             | 
| 317 | 
            -
             | 
| 318 | 
            -
             | 
| 319 | 
            -
             | 
| 320 | 
            -
             | 
| 321 | 
            -
             | 
| 322 | 
            -
             | 
| 323 | 
            -
             | 
| 324 | 
            -
             | 
| 325 | 
            -
             | 
| 326 | 
            -
             | 
| 327 | 
            +
                      unless indexed_fields.empty?
         | 
| 328 | 
            +
                        indexed_fields.map {|key|
         | 
| 329 | 
            +
                          if key.class == String or key.class == Symbol then
         | 
| 330 | 
            +
                            Ampere.connection.hget("ampere.index.#{to_s.downcase}.#{key}", options[key]).split(/:/) #.map {|id| find(id)}
         | 
| 331 | 
            +
                          else
         | 
| 332 | 
            +
                            # Compound index
         | 
| 333 | 
            +
                            Ampere.connection.hget(
         | 
| 334 | 
            +
                              "ampere.index.#{to_s.downcase}.#{key.join(':')}", 
         | 
| 335 | 
            +
                              key.map{|k| options[k]}.join(':')
         | 
| 336 | 
            +
                            ).split(/:/) #.map {|id| find(id)}
         | 
| 337 | 
            +
                          end
         | 
| 338 | 
            +
                        }.each {|s|
         | 
| 339 | 
            +
                          return s if s.empty?
         | 
| 327 340 |  | 
| 328 | 
            -
             | 
| 329 | 
            -
             | 
| 330 | 
            -
             | 
| 331 | 
            -
             | 
| 332 | 
            -
             | 
| 333 | 
            -
                      }
         | 
| 334 | 
            -
                    end
         | 
| 335 | 
            -
                      
         | 
| 336 | 
            -
                    unless nonindexed_fields.empty?
         | 
| 337 | 
            -
                      results = all if results.nil?
         | 
| 338 | 
            -
                      results = results.to_a.map{|r| r.class == String ? find(r) : r}
         | 
| 339 | 
            -
                      nonindexed_fields.each do |key|
         | 
| 340 | 
            -
                        results.select!{|r| 
         | 
| 341 | 
            -
                          r.send(key) == options[key]
         | 
| 341 | 
            +
                          if results.nil? then
         | 
| 342 | 
            +
                            results = s
         | 
| 343 | 
            +
                          else
         | 
| 344 | 
            +
                            results &= s
         | 
| 345 | 
            +
                          end
         | 
| 342 346 | 
             
                        }
         | 
| 343 347 | 
             
                      end
         | 
| 344 | 
            -
             | 
| 348 | 
            +
                      
         | 
| 349 | 
            +
                      unless nonindexed_fields.empty?
         | 
| 350 | 
            +
                        results = all if results.nil?
         | 
| 351 | 
            +
                        results = results.to_a.map{|r| r.class == String ? find(r) : r}
         | 
| 352 | 
            +
                        nonindexed_fields.each do |key|
         | 
| 353 | 
            +
                          results.select!{|r| 
         | 
| 354 | 
            +
                            r.send(key) == options[key]
         | 
| 355 | 
            +
                          }
         | 
| 356 | 
            +
                        end
         | 
| 357 | 
            +
                      end
         | 
| 345 358 |  | 
| 346 | 
            -
             | 
| 347 | 
            -
             | 
| 359 | 
            +
                      # TODO The eval(to_s) trick seems a little... ghetto. 
         | 
| 360 | 
            +
                      Ampere::Collection.new(eval(to_s), results.reverse)
         | 
| 361 | 
            +
                    end
         | 
| 348 362 | 
             
                  end
         | 
| 349 | 
            -
                end
         | 
| 350 363 |  | 
| 351 | 
            -
             | 
| 364 | 
            +
                  private
         | 
| 352 365 |  | 
| 353 | 
            -
             | 
| 354 | 
            -
             | 
| 355 | 
            -
             | 
| 356 | 
            -
             | 
| 366 | 
            +
                  def compound_indices_for(query)
         | 
| 367 | 
            +
                    compound_indices.select{|ci|
         | 
| 368 | 
            +
                      (query.keys - ci).empty?
         | 
| 369 | 
            +
                    }
         | 
| 370 | 
            +
                  end
         | 
| 357 371 | 
             
                end
         | 
| 358 372 |  | 
| 359 | 
            -
                
         | 
| 360 373 | 
             
              end
         | 
| 361 374 |  | 
| 362 375 | 
             
            end
         | 
    
        data/spec/models/indices_spec.rb
    CHANGED
    
    | @@ -1,11 +1,13 @@ | |
| 1 1 | 
             
            require File.join(File.dirname(__FILE__), "..", "spec_helper.rb")
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe "Model indices", :indices => true do
         | 
| 4 | 
            -
              before : | 
| 4 | 
            +
              before :each do
         | 
| 5 5 | 
             
                Redis.new.flushall
         | 
| 6 6 | 
             
                Ampere.connect
         | 
| 7 7 |  | 
| 8 | 
            -
                class Student | 
| 8 | 
            +
                class Student
         | 
| 9 | 
            +
                  include Ampere::Model
         | 
| 10 | 
            +
                  
         | 
| 9 11 | 
             
                  field :first_name
         | 
| 10 12 | 
             
                  field :last_name
         | 
| 11 13 | 
             
                  field :student_id_num
         | 
| @@ -48,19 +50,25 @@ describe "Model indices", :indices => true do | |
| 48 50 |  | 
| 49 51 | 
             
              it 'should refuse to create an index on a field that does not exist' do
         | 
| 50 52 | 
             
                (->{
         | 
| 51 | 
            -
                  class Student | 
| 53 | 
            +
                  class Student
         | 
| 54 | 
            +
                    include Ampere::Model
         | 
| 55 | 
            +
                    
         | 
| 52 56 | 
             
                    field :this_field_exists
         | 
| 53 57 |  | 
| 54 58 | 
             
                    index :this_field_exists
         | 
| 55 59 | 
             
                  end
         | 
| 56 60 | 
             
                }).should_not raise_error
         | 
| 57 61 | 
             
                (->{
         | 
| 58 | 
            -
                  class Student | 
| 62 | 
            +
                  class Student
         | 
| 63 | 
            +
                    include Ampere::Model
         | 
| 64 | 
            +
                    
         | 
| 59 65 | 
             
                    index :this_field_does_not_exist
         | 
| 60 66 | 
             
                  end
         | 
| 61 67 | 
             
                }).should raise_error
         | 
| 62 68 | 
             
                (->{
         | 
| 63 | 
            -
                  class Student | 
| 69 | 
            +
                  class Student
         | 
| 70 | 
            +
                    include Ampere::Model
         | 
| 71 | 
            +
                    
         | 
| 64 72 | 
             
                    field :this_field_exists
         | 
| 65 73 |  | 
| 66 74 | 
             
                    index [:this_field_exists, :but_this_one_does_not]
         | 
| @@ -68,7 +76,7 @@ describe "Model indices", :indices => true do | |
| 68 76 | 
             
                }).should raise_error
         | 
| 69 77 | 
             
              end
         | 
| 70 78 |  | 
| 71 | 
            -
              it 'should enforce the uniqueness of unique single-field indices' | 
| 79 | 
            +
              it 'should enforce the uniqueness of unique single-field indices' do
         | 
| 72 80 | 
             
                # The student_id_num field of Student is unique. If two Students
         | 
| 73 81 | 
             
                # with the same student_id_num are stored, the second should not 
         | 
| 74 82 | 
             
                # save successfully, throwing an exception instead.
         | 
| @@ -87,7 +95,9 @@ describe "Model indices", :indices => true do | |
| 87 95 |  | 
| 88 96 | 
             
              context 'compound indices' do
         | 
| 89 97 | 
             
                before :all do
         | 
| 90 | 
            -
                  class Professor | 
| 98 | 
            +
                  class Professor
         | 
| 99 | 
            +
                    include Ampere::Model
         | 
| 100 | 
            +
                    
         | 
| 91 101 | 
             
                    field :first_name
         | 
| 92 102 | 
             
                    field :last_name
         | 
| 93 103 | 
             
                    field :employee_id_number
         | 
    
        data/spec/models/model_spec.rb
    CHANGED
    
    | @@ -9,7 +9,9 @@ describe "Base models", :model => true do | |
| 9 9 | 
             
                  Redis.new.flushall
         | 
| 10 10 |  | 
| 11 11 | 
             
                  # Define a model class here.
         | 
| 12 | 
            -
                  class Post | 
| 12 | 
            +
                  class Post
         | 
| 13 | 
            +
                    include Ampere::Model
         | 
| 14 | 
            +
                    
         | 
| 13 15 | 
             
                    field :title
         | 
| 14 16 | 
             
                    field :byline
         | 
| 15 17 | 
             
                    field :content
         | 
| @@ -37,7 +39,9 @@ describe "Base models", :model => true do | |
| 37 39 | 
             
                  end
         | 
| 38 40 |  | 
| 39 41 | 
             
                  it "should have default values definable" do
         | 
| 40 | 
            -
                    class Comment | 
| 42 | 
            +
                    class Comment
         | 
| 43 | 
            +
                      include Ampere::Model
         | 
| 44 | 
            +
                      
         | 
| 41 45 | 
             
                      field :subject, :default => "No subject"
         | 
| 42 46 | 
             
                      field :content
         | 
| 43 47 | 
             
                    end
         | 
| @@ -54,7 +58,9 @@ describe "Base models", :model => true do | |
| 54 58 | 
             
                  context 'types', :types => true do
         | 
| 55 59 | 
             
                    before :all do
         | 
| 56 60 | 
             
                      # Just adding a field with a type to Post
         | 
| 57 | 
            -
                      class Post | 
| 61 | 
            +
                      class Post
         | 
| 62 | 
            +
                        include Ampere::Model
         | 
| 63 | 
            +
                        
         | 
| 58 64 | 
             
                        field :pageviews, :type => Integer, :default => 0
         | 
| 59 65 | 
             
                      end
         | 
| 60 66 | 
             
                    end
         | 
    
        data/spec/models/queries_spec.rb
    CHANGED
    
    
| @@ -6,7 +6,9 @@ describe 'belongs_to relationships', :belongs_to => true do | |
| 6 6 | 
             
                Ampere.connect
         | 
| 7 7 |  | 
| 8 8 | 
             
                # These are used by the has_one/belongs_to example below
         | 
| 9 | 
            -
                class Car | 
| 9 | 
            +
                class Car
         | 
| 10 | 
            +
                  include Ampere::Model
         | 
| 11 | 
            +
                  
         | 
| 10 12 | 
             
                  field :make
         | 
| 11 13 | 
             
                  field :model
         | 
| 12 14 | 
             
                  field :year
         | 
| @@ -15,7 +17,9 @@ describe 'belongs_to relationships', :belongs_to => true do | |
| 15 17 | 
             
                  has_many :passengers
         | 
| 16 18 | 
             
                end
         | 
| 17 19 |  | 
| 18 | 
            -
                class Engine | 
| 20 | 
            +
                class Engine
         | 
| 21 | 
            +
                  include Ampere::Model
         | 
| 22 | 
            +
                  
         | 
| 19 23 | 
             
                  field :displacement
         | 
| 20 24 | 
             
                  field :cylinders
         | 
| 21 25 | 
             
                  field :configuration
         | 
| @@ -23,7 +27,9 @@ describe 'belongs_to relationships', :belongs_to => true do | |
| 23 27 | 
             
                  belongs_to :car
         | 
| 24 28 | 
             
                end
         | 
| 25 29 |  | 
| 26 | 
            -
                class Passenger | 
| 30 | 
            +
                class Passenger
         | 
| 31 | 
            +
                  include Ampere::Model
         | 
| 32 | 
            +
                  
         | 
| 27 33 | 
             
                  field :name
         | 
| 28 34 | 
             
                  field :seat
         | 
| 29 35 |  | 
| @@ -6,7 +6,9 @@ describe 'has_many relationships', :has_many => true do | |
| 6 6 | 
             
                Ampere.connect
         | 
| 7 7 |  | 
| 8 8 | 
             
                # These are used by the has_one/belongs_to example below
         | 
| 9 | 
            -
                class Car | 
| 9 | 
            +
                class Car
         | 
| 10 | 
            +
                  include Ampere::Model
         | 
| 11 | 
            +
                  
         | 
| 10 12 | 
             
                  field :make
         | 
| 11 13 | 
             
                  field :model
         | 
| 12 14 | 
             
                  field :year
         | 
| @@ -14,7 +16,9 @@ describe 'has_many relationships', :has_many => true do | |
| 14 16 | 
             
                  has_many :passengers
         | 
| 15 17 | 
             
                end
         | 
| 16 18 |  | 
| 17 | 
            -
                class Passenger | 
| 19 | 
            +
                class Passenger
         | 
| 20 | 
            +
                  include Ampere::Model
         | 
| 21 | 
            +
                  
         | 
| 18 22 | 
             
                  field :name
         | 
| 19 23 | 
             
                  field :seat
         | 
| 20 24 |  | 
| @@ -6,7 +6,9 @@ describe 'has_one relationships', has_one:true do | |
| 6 6 | 
             
                Ampere.connect
         | 
| 7 7 |  | 
| 8 8 | 
             
                # These are used by the has_one/belongs_to example below
         | 
| 9 | 
            -
                class Car | 
| 9 | 
            +
                class Car
         | 
| 10 | 
            +
                  include Ampere::Model
         | 
| 11 | 
            +
                  
         | 
| 10 12 | 
             
                  field :make
         | 
| 11 13 | 
             
                  field :model
         | 
| 12 14 | 
             
                  field :year
         | 
| @@ -14,7 +16,9 @@ describe 'has_one relationships', has_one:true do | |
| 14 16 | 
             
                  has_one :engine
         | 
| 15 17 | 
             
                end
         | 
| 16 18 |  | 
| 17 | 
            -
                class Engine | 
| 19 | 
            +
                class Engine
         | 
| 20 | 
            +
                  include Ampere::Model
         | 
| 21 | 
            +
                  
         | 
| 18 22 | 
             
                  field :displacement
         | 
| 19 23 | 
             
                  field :cylinders
         | 
| 20 24 | 
             
                  field :configuration
         | 
    
        data/spec/models/updates_spec.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ampere
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 1.0.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,11 +9,11 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012- | 
| 12 | 
            +
            date: 2012-04-20 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: redis
         | 
| 16 | 
            -
              requirement: & | 
| 16 | 
            +
              requirement: &70280166250520 !ruby/object:Gem::Requirement
         | 
| 17 17 | 
             
                none: false
         | 
| 18 18 | 
             
                requirements:
         | 
| 19 19 | 
             
                - - ! '>='
         | 
| @@ -21,10 +21,10 @@ dependencies: | |
| 21 21 | 
             
                    version: '0'
         | 
| 22 22 | 
             
              type: :runtime
         | 
| 23 23 | 
             
              prerelease: false
         | 
| 24 | 
            -
              version_requirements: * | 
| 24 | 
            +
              version_requirements: *70280166250520
         | 
| 25 25 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 26 26 | 
             
              name: shoulda
         | 
| 27 | 
            -
              requirement: & | 
| 27 | 
            +
              requirement: &70280166249600 !ruby/object:Gem::Requirement
         | 
| 28 28 | 
             
                none: false
         | 
| 29 29 | 
             
                requirements:
         | 
| 30 30 | 
             
                - - ! '>='
         | 
| @@ -32,10 +32,10 @@ dependencies: | |
| 32 32 | 
             
                    version: '0'
         | 
| 33 33 | 
             
              type: :development
         | 
| 34 34 | 
             
              prerelease: false
         | 
| 35 | 
            -
              version_requirements: * | 
| 35 | 
            +
              version_requirements: *70280166249600
         | 
| 36 36 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 37 37 | 
             
              name: cucumber
         | 
| 38 | 
            -
              requirement: & | 
| 38 | 
            +
              requirement: &70280166263980 !ruby/object:Gem::Requirement
         | 
| 39 39 | 
             
                none: false
         | 
| 40 40 | 
             
                requirements:
         | 
| 41 41 | 
             
                - - ! '>='
         | 
| @@ -43,10 +43,10 @@ dependencies: | |
| 43 43 | 
             
                    version: '0'
         | 
| 44 44 | 
             
              type: :development
         | 
| 45 45 | 
             
              prerelease: false
         | 
| 46 | 
            -
              version_requirements: * | 
| 46 | 
            +
              version_requirements: *70280166263980
         | 
| 47 47 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 48 | 
             
              name: bundler
         | 
| 49 | 
            -
              requirement: & | 
| 49 | 
            +
              requirement: &70280166262680 !ruby/object:Gem::Requirement
         | 
| 50 50 | 
             
                none: false
         | 
| 51 51 | 
             
                requirements:
         | 
| 52 52 | 
             
                - - ~>
         | 
| @@ -54,10 +54,10 @@ dependencies: | |
| 54 54 | 
             
                    version: 1.0.0
         | 
| 55 55 | 
             
              type: :development
         | 
| 56 56 | 
             
              prerelease: false
         | 
| 57 | 
            -
              version_requirements: * | 
| 57 | 
            +
              version_requirements: *70280166262680
         | 
| 58 58 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 59 59 | 
             
              name: jeweler
         | 
| 60 | 
            -
              requirement: & | 
| 60 | 
            +
              requirement: &70280166261440 !ruby/object:Gem::Requirement
         | 
| 61 61 | 
             
                none: false
         | 
| 62 62 | 
             
                requirements:
         | 
| 63 63 | 
             
                - - ~>
         | 
| @@ -65,10 +65,10 @@ dependencies: | |
| 65 65 | 
             
                    version: 1.6.4
         | 
| 66 66 | 
             
              type: :development
         | 
| 67 67 | 
             
              prerelease: false
         | 
| 68 | 
            -
              version_requirements: * | 
| 68 | 
            +
              version_requirements: *70280166261440
         | 
| 69 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 70 70 | 
             
              name: simplecov
         | 
| 71 | 
            -
              requirement: & | 
| 71 | 
            +
              requirement: &70280166260320 !ruby/object:Gem::Requirement
         | 
| 72 72 | 
             
                none: false
         | 
| 73 73 | 
             
                requirements:
         | 
| 74 74 | 
             
                - - ! '>='
         | 
| @@ -76,10 +76,10 @@ dependencies: | |
| 76 76 | 
             
                    version: '0'
         | 
| 77 77 | 
             
              type: :development
         | 
| 78 78 | 
             
              prerelease: false
         | 
| 79 | 
            -
              version_requirements: * | 
| 79 | 
            +
              version_requirements: *70280166260320
         | 
| 80 80 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 81 81 | 
             
              name: rspec
         | 
| 82 | 
            -
              requirement: & | 
| 82 | 
            +
              requirement: &70280166259660 !ruby/object:Gem::Requirement
         | 
| 83 83 | 
             
                none: false
         | 
| 84 84 | 
             
                requirements:
         | 
| 85 85 | 
             
                - - ! '>='
         | 
| @@ -87,10 +87,10 @@ dependencies: | |
| 87 87 | 
             
                    version: '0'
         | 
| 88 88 | 
             
              type: :development
         | 
| 89 89 | 
             
              prerelease: false
         | 
| 90 | 
            -
              version_requirements: * | 
| 90 | 
            +
              version_requirements: *70280166259660
         | 
| 91 91 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 92 92 | 
             
              name: rdoc
         | 
| 93 | 
            -
              requirement: & | 
| 93 | 
            +
              requirement: &70280166257860 !ruby/object:Gem::Requirement
         | 
| 94 94 | 
             
                none: false
         | 
| 95 95 | 
             
                requirements:
         | 
| 96 96 | 
             
                - - ! '>='
         | 
| @@ -98,7 +98,7 @@ dependencies: | |
| 98 98 | 
             
                    version: '0'
         | 
| 99 99 | 
             
              type: :development
         | 
| 100 100 | 
             
              prerelease: false
         | 
| 101 | 
            -
              version_requirements: * | 
| 101 | 
            +
              version_requirements: *70280166257860
         | 
| 102 102 | 
             
            description: An ActiveRecord/Mongoid-esque object model for the Redis key/value data
         | 
| 103 103 | 
             
              store.
         | 
| 104 104 | 
             
            email: max@villainousindustri.es
         | 
| @@ -151,7 +151,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 151 151 | 
             
                  version: '0'
         | 
| 152 152 | 
             
                  segments:
         | 
| 153 153 | 
             
                  - 0
         | 
| 154 | 
            -
                  hash:  | 
| 154 | 
            +
                  hash: 719359362580818516
         | 
| 155 155 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 156 156 | 
             
              none: false
         | 
| 157 157 | 
             
              requirements:
         | 
| @@ -160,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 160 160 | 
             
                  version: '0'
         | 
| 161 161 | 
             
            requirements: []
         | 
| 162 162 | 
             
            rubyforge_project: 
         | 
| 163 | 
            -
            rubygems_version: 1.8. | 
| 163 | 
            +
            rubygems_version: 1.8.17
         | 
| 164 164 | 
             
            signing_key: 
         | 
| 165 165 | 
             
            specification_version: 3
         | 
| 166 166 | 
             
            summary: A pure Ruby ORM for Redis.
         |