Skip to content

Commit

Permalink
Merge pull request ionia-corporation#83 from DanOlson/fix-nil-belongs-to
Browse files Browse the repository at this point in the history
Nil return value on belongs_to association should be handled gracefully
  • Loading branch information
Oldani Pablo committed Sep 29, 2014
2 parents 8b924bb + 536a57f commit 0fe9d34
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 49 deletions.
1 change: 1 addition & 0 deletions lib/active_force/association.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'active_force/association/association'
require 'active_force/association/eager_load_projection_builder'
require 'active_force/association/relation_model_builder'
require 'active_force/association/has_many_association'
require 'active_force/association/belongs_to_association'

Expand Down
12 changes: 5 additions & 7 deletions lib/active_force/association/association.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
module ActiveForce
module Association
class Association
extend Forwardable
def_delegators :relation_model, :build

attr_accessor :options, :relation_name

def initialize parent, relation_name, options
def initialize parent, relation_name, options = {}
@parent = parent
@relation_name = relation_name
@options = options
build
define_relation_method
end

def relation_model
Expand All @@ -29,7 +31,7 @@ def relationship_name
# could be 'Quota__r' or 'Account'.
def represents_sfdc_table?(sfdc_table_name)
name = sfdc_table_name.sub(/__r\z/, '').singularize
relationship_name.sub(/__c\z/, '') == name
relationship_name.sub(/__c\z|__r\z/, '') == name
end

def sfdc_association_field
Expand All @@ -38,10 +40,6 @@ def sfdc_association_field

private

def build
define_relation_method
end

def infer_foreign_key_from_model(model)
name = model.custom_table? ? model.name : model.table_name
"#{name.downcase}_id".to_sym
Expand Down
70 changes: 70 additions & 0 deletions lib/active_force/association/relation_model_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
module ActiveForce
module Association
class RelationModelBuilder
class << self
def build(association, value)
new(association, value).build_relation_model
end
end

def initialize(association, value)
@association = association
@value = value
end

def build_relation_model
klass = resolve_class
klass.new(@association, @value).call
end

private

def resolve_class
association_builder = @value.class.name.gsub('::', '_')
ActiveForce::Association.const_get "BuildFrom#{association_builder}"
rescue NameError
raise "Don't know how to build relation from #{@value.class.name}"
end
end

class AbstractBuildFrom
attr_reader :association, :value

def initialize(association, value)
@association = association
@value = value
end

def call
raise "Must implement #{self.class.name}#call"
end
end

class BuildFromHash < AbstractBuildFrom
def call
association.build value
end
end

class BuildFromArray < AbstractBuildFrom
def call
value.map { |mash| association.build mash }
end
end

class BuildFromNilClass < AbstractBuildFrom
def call
association.is_a?(BelongsToAssociation) ? nil : []
end
end

class BuildFromRestforce_SObject < BuildFromHash
end

class BuildFromRestforce_Mash < BuildFromHash
end

class BuildFromRestforce_Collection < BuildFromArray
end
end
end
8 changes: 2 additions & 6 deletions lib/active_force/sobject.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,9 @@ def reload
end

def write_value column, value
if [Hash, Array, NilClass].include?(value.class) and association = self.class.find_association(column)
if association = self.class.find_association(column)
field = association.relation_name
value = case value
when Hash; association.relation_model.build value
when Array; value.map { |mash| association.relation_model.build mash }
when NilClass; []
end
value = Association::RelationModelBuilder.build(association, value)
else
field = mappings.invert[column]
value = self.class.mapping.translate_value value, field unless value.nil?
Expand Down
62 changes: 62 additions & 0 deletions spec/active_force/association/relation_model_builder_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'spec_helper'

module ActiveForce
module Association
describe RelationModelBuilder do
let(:instance){ described_class.new association, value }

describe '#build_relation_model' do
context 'has_many' do
let(:association){ HasManyAssociation.new Post, :comments }

context 'with values' do
let(:value) do
build_restforce_collection([
Restforce::SObject.new({'Id' => '213', 'PostId' => '123'}),
Restforce::SObject.new({'Id' => '214', 'PostId' => '123'})
])
end

it 'returns an array of Comments' do
comments = instance.build_relation_model
expect(comments).to be_a Array
expect(comments.all?{ |c| c.is_a? Comment }).to be true
end
end

context 'without values' do
let(:value){ nil }

it 'returns an empty array' do
comments = instance.build_relation_model
expect(comments).to be_a Array
expect(comments).to be_empty
end
end
end

context 'belongs_to' do
let(:association){ BelongsToAssociation.new(Comment, :post) }

context 'with a value' do
let(:value) do
build_restforce_sobject 'Id' => '213'
end

it 'returns a post' do
expect(instance.build_relation_model).to be_a Post
end
end

context 'without a value' do
let(:value){ nil }

it 'returns nil' do
expect(instance.build_relation_model).to be_nil
end
end
end
end
end
end
end
Loading

0 comments on commit 0fe9d34

Please sign in to comment.