Commit 9d11dbb

mo khan <mo@mokhan.ca>
2014-07-12 19:47:30
load a has_many relationship.
1 parent 8d4e52f
lib/humble/column.rb
@@ -15,7 +15,8 @@ module Humble
       @column_name == column_name
     end
 
-    def apply(value, entity, session)
+    def apply(row, entity, session)
+      value = row[column_name]
       entity.public_send("#{@column_name}=", value)
     end
 
@@ -51,7 +52,8 @@ module Humble
       @type = type
     end
 
-    def apply(value, entity, session)
+    def apply(row, entity, session)
+      value = row[column_name]
       entity.public_send("#{attribute_name}=", session.find(@type, value))
     end
 
@@ -65,4 +67,20 @@ module Humble
       column_name.to_s.gsub(/_id/, '')
     end
   end
+
+  class HasMany < Column
+    def initialize(attribute, type)
+      super(attribute)
+      @attribute, @type = attribute, type
+    end
+
+    def apply(row, entity, session)
+      items = session.find_all(@type)
+      entity.public_send("#{@attribute}=", items)
+    end
+
+    def prepare(entity)
+      { }
+    end
+  end
 end
lib/humble/database_table.rb
@@ -32,10 +32,6 @@ module Humble
       end
     end
 
-    def column_for(key)
-      @columns.find { |x| x.matches?(key) }
-    end
-
     def prepare_statement_for(item)
       @columns.inject({}) do |result, column|
         result.merge(column.prepare(item))
lib/humble/mapping_configuration.rb
@@ -12,8 +12,8 @@ module Humble
     def save_using(session, entity)
       connection = session.create_connection[@table.name]
       if primary_key.has_default_value?(entity)
-        result = connection.insert(@table.prepare_statement_for(entity))
-        primary_key.apply(result, entity, session)
+        connection.insert(@table.prepare_statement_for(entity))
+        primary_key.apply(connection, entity, session)
       else
         connection.update(@table.prepare_statement_for(entity))
       end
@@ -40,17 +40,19 @@ module Humble
     end
 
     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|
-          row.each do |key, value|
-            @table.column_for(key).apply(value, entity, @session)
-          end
+        entity = table.type.new
+        table.each do |column|
+          column.apply(row, entity, session)
         end
+        entity
       end
     end
   end
lib/humble/mapping_configuration_builder.rb
@@ -20,6 +20,10 @@ module Humble
       @table.add(BelongsTo.new(foreign_key, type))
     end
 
+    def has_many(attribute_name, type)
+      @table.add(HasMany.new(attribute_name, type))
+    end
+
     def build(mapping)
       @table = DatabaseTable.new
       mapping.run(self)
lib/humble/session.rb
@@ -16,7 +16,9 @@ module Humble
     end
 
     def find(clazz, id)
-      find_all(clazz).find { |x| x.id == id }
+      find_all(clazz).find do |x|
+        x.id == id
+      end
     end
 
     def find_all(clazz)
spec/integration/fixtures/movie_mapping.rb
@@ -1,5 +1,5 @@
 class Movie
-  attr_accessor :id, :name, :studio
+  attr_accessor :id, :name, :studio, :reviews
 
   def ==(other)
     return false unless other
@@ -16,5 +16,6 @@ class MovieMapping < Humble::DatabaseMapping
     map.primary_key(:id, default: -1)
     map.column :name
     map.belongs_to :studio_id, Studio
+    map.has_many :reviews, Review
   end
 end
spec/integration/fixtures/review_mapping.rb
@@ -0,0 +1,13 @@
+class Review
+  attr_accessor :id, :description, :movie
+end
+
+class ReviewMapping < Humble::DatabaseMapping
+  def run(map)
+    map.table :reviews
+    map.type Review
+    map.primary_key(:id, default: -1)
+    map.column :description
+    map.belongs_to :movie_id, Movie
+  end
+end
spec/integration/select_spec.rb
@@ -5,7 +5,7 @@ describe "select items" do
 
   context "when fetching all items" do
     before :each do
-      @id = connection[:movies].insert(:name => 'monsters inc')
+      @id = connection[:movies].insert(name: 'monsters inc')
     end
 
     let(:results) { session.find_all Movie }
@@ -32,6 +32,11 @@ describe "select items" 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(: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)
@@ -50,7 +55,10 @@ describe "select items" do
       expect(result.studio.name).to eql('universal')
     end
 
-    xit "loads the has_many association" do
+    it "loads a has_many association" do
+      expect(result.reviews).to_not be_nil
+      expect(result.reviews.first.description).to eql(description)
+      expect(result.reviews.first.description).to eql(description)
     end
   end
 end
spec/integration_helper.rb
@@ -2,6 +2,7 @@ require "spec_helper"
 require 'sequel'
 require_relative 'integration/fixtures/studio_mapping.rb'
 require_relative 'integration/fixtures/movie_mapping.rb'
+require_relative 'integration/fixtures/review_mapping.rb'
 
 shared_context "orm" do
   let(:connection) { Sequel.connect(connection_string) }
@@ -22,12 +23,20 @@ shared_context "orm" do
       String :name
     end
 
+    connection.create_table :reviews do
+      primary_key :id
+      BigNum :movie_id
+      String :description
+    end
+
     configuration.add(MovieMapping.new)
     configuration.add(StudioMapping.new)
+    configuration.add(ReviewMapping.new)
   end
 
   after :each do
     connection.drop_table :studios
     connection.drop_table :movies
+    connection.drop_table :reviews
   end
 end