Commit b820d32

mo khan <mo@mokhan.ca>
2014-07-13 15:31:41
move classes to separate files and startto build identity map.
1 parent 9d11dbb
lib/humble/column.rb
@@ -75,7 +75,8 @@ module Humble
     end
 
     def apply(row, entity, session)
-      items = session.find_all(@type)
+      puts "#{@attribute} #{@type} #{row} #{entity}"
+      items = session.find_all(@type).to_a
       entity.public_send("#{@attribute}=", items)
     end
 
lib/humble/default_mapper.rb
@@ -0,0 +1,16 @@
+class DefaultMapper
+  attr_reader :session, :table
+
+  def initialize(table, session)
+    @table = table
+    @session = session
+  end
+
+  def map_from(row)
+    table.type.new.tap do |entity|
+      table.each do |column|
+        column.apply(row, entity, session)
+      end
+    end
+  end
+end
lib/humble/identity_map.rb
@@ -0,0 +1,14 @@
+class IdentityMap
+  def initialize(items = {})
+    @items = items
+  end
+
+  def fetch(key, &block)
+    if @items.key?(key)
+      @items[key]
+    else
+      @items[key] = block.call
+      @items[key]
+    end
+  end
+end
lib/humble/mapping_configuration.rb
@@ -1,14 +1,12 @@
 module Humble
   class MappingConfiguration
+    attr_reader :table
+
     def initialize(table, configuration)
       @table = table
       @configuration = configuration
     end
 
-    def find_all_using(session)
-      ResultSet.new(session.create_connection[@table.name], mapper_for(session))
-    end
-
     def save_using(session, entity)
       connection = session.create_connection[@table.name]
       if primary_key.has_default_value?(entity)
@@ -29,31 +27,10 @@ module Humble
 
     private
 
-    def mapper_for(session)
-      DefaultMapper.new(@table, session)
-    end
-
     def primary_key
       @primary_key ||= @table.find do |column|
         column.primary_key?
       end
     end
-
-    class DefaultMapper
-      attr_reader :session, :table
-
-      def initialize(table, session)
-        @table = table
-        @session = session
-      end
-
-      def map_from(row)
-        entity = table.type.new
-        table.each do |column|
-          column.apply(row, entity, session)
-        end
-        entity
-      end
-    end
   end
 end
lib/humble/result_set.rb
@@ -8,19 +8,27 @@ module Humble
     end
 
     def each(&block)
-      @rows.each do |row|
-        block.call(@mapper.map_from(row))
+      items.each do |item|
+        block.call(item)
       end
     end
 
     def include?(item)
-      self.find do |x|
+      find do |x|
         x == item
       end
     end
 
     def inspect
-      "[#{self.map { |x| x.inspect }.join(", ")}]"
+      "[#{map { |x| x.inspect }.join(", ")}]"
+    end
+
+    private
+
+    def items
+      @items ||= @rows.map do |row|
+        @mapper.map_from(row)
+      end.to_a
     end
   end
 end
lib/humble/session.rb
@@ -3,6 +3,7 @@ module Humble
     def initialize(session_factory, configuration)
       @session_factory = session_factory
       @configuration = configuration
+      @identity_map = IdentityMap.new
     end
 
     def begin_transaction(&block)
@@ -16,13 +17,16 @@ module Humble
     end
 
     def find(clazz, id)
-      find_all(clazz).find do |x|
-        x.id == id
+      @identity_map.fetch("#{clazz.name}-#{id}") do
+        find_all(clazz).find do |x|
+          x.id == id
+        end
       end
     end
 
     def find_all(clazz)
-      mapping_for(clazz).find_all_using(self)
+      table = mapping_for(clazz).table
+      ResultSet.new(create_connection[table.name], DefaultMapper.new(table, self))
     end
 
     def delete(entity)
lib/humble.rb
@@ -9,6 +9,8 @@ require "humble/mapping_configuration"
 require "humble/mapping_configuration_builder"
 require "humble/database_table"
 require "humble/column"
+require "humble/default_mapper"
+require "humble/identity_map"
 
 module Humble
 
spec/integration/select_spec.rb
@@ -31,13 +31,11 @@ describe "select items" do
   context "when fetching a single item" do
     let!(:studio_id) { connection[:studios].insert(name: 'universal') }
     let!(:movie_id) { connection[:movies].insert(name: 'blood in, blood out', studio_id: studio_id) }
+    let!(:review_id) { connection[:reviews].insert(movie_id: movie_id, description: description) }
+    let!(:other_review_id) { connection[:reviews].insert(movie_id: movie_id + 1, description: 'blah blah') }
     let(:result) { session.find(Movie, movie_id) }
     let(:description) { 'wow... that snail is fast.' }
 
-    before :each do
-      connection[:reviews].insert(movie_id: movie_id, description: description)
-    end
-
     it "loads the proper type" do
       expect(result).to be_instance_of(Movie)
     end
@@ -60,5 +58,9 @@ describe "select items" do
       expect(result.reviews.first.description).to eql(description)
       expect(result.reviews.first.description).to eql(description)
     end
+
+    it "does not load items associated with another parent record" do
+      expect(result.reviews.find { |x| x.id == other_review_id }).to be_nil
+    end
   end
 end
humble.gemspec
@@ -23,4 +23,5 @@ Gem::Specification.new do |spec|
   spec.add_development_dependency "rake"
   spec.add_development_dependency 'rspec'
   spec.add_development_dependency 'sqlite3'
+  spec.add_development_dependency 'byebug'
 end