Skip to content

Commit

Permalink
Merge branch 'refs/heads/my_master' into where_parentheses
Browse files Browse the repository at this point in the history
Conflicts:
	spec/active_force/association_spec.rb
	spec/active_force/query_spec.rb
	spec/active_force/sobject/includes_spec.rb
  • Loading branch information
Lyric Hupp committed Oct 8, 2014
2 parents c5ed4f2 + 2b52fad commit ec6a53d
Show file tree
Hide file tree
Showing 12 changed files with 279 additions and 98 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changelog

## Not released

* Rails4-style conditional has_many associations ([Dan Olson][])
* Add `#includes` query method to eager load has_many association. ([Dan Olson][])
* Add `#includes` query method to eager load belongs_to association. ([#65][])
* SObject#destroy method.
Expand Down
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ class Medication < ActiveForce::SObject

field :name, from: 'Name'

field :max_dossage # from defaults to "Max_Dossage__c"
field :max_dossage # defaults to "Max_Dossage__c"
field :updated_from

##
# Table name is infered from class name.
# Table name is inferred from class name.
#
# self.table_name = 'Medication__c' # default one.

Expand Down Expand Up @@ -90,19 +90,17 @@ Altenative you can try the generator. (requires setting up the connection)
class Account < ActiveForce::SObject
has_many :pages

# Use option parameters in the declaration.
# Use optional parameters in the declaration.

has_many :medications,
where: "Discontinued__c > #{ Date.today.strftime("%Y-%m-%d") }" \
"OR Discontinued__c = NULL"
scoped_as: ->{ where("Discontinued__c > ? OR Discontinued__c = ?", Date.today.strftime("%Y-%m-%d"), nil) }

has_many :today_log_entries,
model: DailyLogEntry,
where: { date: Time.now.in_time_zone.strftime("%Y-%m-%d") }
scoped_as: ->{ where(date: Time.now.in_time_zone.strftime("%Y-%m-%d") }

has_many :labs,
where: "Category__c = 'EMR' AND Date__c <> NULL",
order: 'Date__c DESC'
scoped_as: ->{ where("Category__c = 'EMR' AND Date__c <> NULL").order('Date__c DESC') }

end
```
Expand Down Expand Up @@ -130,7 +128,7 @@ Account.where(web_enable: 1, contact_by: ['web', 'email']).limit(2)
# LIMIT 2
```

This is also possible to eager load belongs_to associations.
It is also possible to eager load associations:

```ruby
Comment.includes(:post)
Expand Down
2 changes: 2 additions & 0 deletions lib/active_force/association/eager_load_projection_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ def projections
klass = association.class.name.split('::').last
builder_class = ActiveForce::Association.const_get "#{klass}ProjectionBuilder"
builder_class.new(association).projections
rescue NameError
raise "Don't know how to build projections for #{klass}"
end
end

Expand Down
8 changes: 7 additions & 1 deletion lib/active_force/association/has_many_association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ def define_relation_method
@parent.send :define_method, _method do
association_cache.fetch _method do
query = association.relation_model.query
query.options association.options
if scope = association.options[:scoped_as]
if scope.arity > 0
query.instance_exec self, &scope
else
query.instance_exec &scope
end
end
association_cache[_method] = query.where association.foreign_key => self.id
end
end
Expand Down
6 changes: 0 additions & 6 deletions lib/active_force/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ def count
self
end

def options args
where args[:where]
limit args[:limit]
order args[:order]
end

protected
def build_select
@query_fields.compact.uniq.join(', ')
Expand Down
76 changes: 49 additions & 27 deletions lib/active_force/sobject.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
require 'restforce'

module ActiveForce
class RecordInvalid < StandardError;end

class SObject
include ActiveAttr::Model
include ActiveAttr::Dirty
Expand All @@ -22,7 +24,7 @@ class SObject

class << self
extend Forwardable
def_delegators :query, :where, :first, :last, :all, :find, :find_by, :count, :includes
def_delegators :query, :where, :first, :last, :all, :find, :find_by, :count, :includes, :limit, :order, :select
def_delegators :mapping, :table, :table_name, :custom_table?, :mappings

private
Expand Down Expand Up @@ -59,63 +61,70 @@ def self.build mash

def update_attributes! attributes = {}
assign_attributes attributes
return false unless valid?
sfdc_client.update! table_name, attributes_for_sfdb
changed_attributes.clear
self
validate!
run_callbacks :save do
run_callbacks :update do
sfdc_client.update! table_name, attributes_for_sfdb
changed_attributes.clear
end
end
true
end

alias_method :update!, :update_attributes!

def update_attributes attributes = {}
run_callbacks :update do
update_attributes! attributes
end
rescue Faraday::Error::ClientError => error
logger_output __method__, error, attributes
update_attributes! attributes
rescue Faraday::Error::ClientError, RecordInvalid => error
handle_save_error error
end

alias_method :update, :update_attributes

def create!
return false unless valid?
self.id = sfdc_client.create! table_name, attributes_for_sfdb
changed_attributes.clear
validate!
run_callbacks :save do
run_callbacks :create do
self.id = sfdc_client.create! table_name, attributes_for_sfdb
changed_attributes.clear
end
end
self
end

def create
run_callbacks :create do
create!
end
rescue Faraday::Error::ClientError => error
logger_output __method__, error, attributes_for_sfdb
create!
rescue Faraday::Error::ClientError, RecordInvalid => error
handle_save_error error
self
end

def destroy
sfdc_client.destroy! self.class.table_name, id
end

def self.create args
new(args).save
new(args).create
end

def self.create! args
new(args).save!
new(args).create!
end

def save
def save!
run_callbacks :save do
if persisted?
update
!!update!
else
create
!!create!
end
end
end

def save!
save
rescue Faraday::Error::ClientError => error
logger_output __method__, error, attributes_for_sfdb
def save
save!
rescue Faraday::Error::ClientError, RecordInvalid => error
handle_save_error error
end

def to_param
Expand Down Expand Up @@ -152,6 +161,19 @@ def write_value column, value

private

def validate!
unless valid?
raise RecordInvalid.new(
"Validation failed: #{errors.full_messages.join(', ')}"
)
end
end

def handle_save_error error
return false if error.class == RecordInvalid
logger_output __method__, error, attributes
end

def association_cache
@association_cache ||= {}
end
Expand Down
26 changes: 18 additions & 8 deletions spec/active_force/association_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

describe ActiveForce::SObject do
let :post do
Post.new(id: "1")
Post.new(id: "1", title: 'Ham')
end

let :comment do
Expand Down Expand Up @@ -34,7 +34,7 @@

describe 'to_s' do
it "should return a SOQL statment" do
soql = "SELECT Id, PostId, PosterId__c, FancyPostId FROM Comment__c WHERE (PostId = '1')"
soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PostId = '1')"
expect(post.comments.to_s).to eq soql
end
end
Expand All @@ -56,25 +56,35 @@

describe 'has_many(options)' do
it 'should allow to send a different query table name' do
soql = "SELECT Id, PostId, PosterId__c, FancyPostId FROM Comment__c WHERE (PostId = '1')"
soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PostId = '1')"
expect(post.ugly_comments.to_s).to eq soql
end

it 'should allow to change the foreign key' do
soql = "SELECT Id, PostId, PosterId__c, FancyPostId FROM Comment__c WHERE (PosterId__c = '1')"
soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PosterId__c = '1')"
expect(post.poster_comments.to_s).to eq soql
end

it 'should allow to add a where condition' do
soql = "SELECT Id, PostId, PosterId__c, FancyPostId FROM Comment__c WHERE (1 = 0) AND (PostId = '1')"
soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (1 = 0) AND (PostId = '1')"
expect(post.impossible_comments.to_s).to eq soql
end

it 'accepts custom scoping' do
soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (Body__c = 'RE: Ham') AND (PostId = '1') ORDER BY CreationDate DESC"
expect(post.reply_comments.to_s).to eq soql
end

it 'accepts custom scoping that preloads associations of the association' do
account = Salesforce::Account.new id: '1', business_partner: 'qwerty'
soql = "SELECT Id, OwnerId, AccountId, Business_Partner__c, Owner.Id FROM Opportunity WHERE (Business_Partner__c = 'qwerty') AND (AccountId = '1')"
expect(account.partner_opportunities.to_s).to eq soql
end

it 'should use a convention name for the foreign key' do
soql = "SELECT Id, PostId, PosterId__c, FancyPostId FROM Comment__c WHERE (PostId = '1')"
soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PostId = '1')"
expect(post.comments.to_s).to eq soql
end

end

describe "belongs_to" do
Expand All @@ -85,7 +95,7 @@
it "should allow to pass a foreign key as options" do
Comment.belongs_to :post, foreign_key: :fancy_post_id
allow(comment).to receive(:fancy_post_id).and_return "2"
expect(client).to receive(:query).with("SELECT Id FROM Post__c WHERE (Id = '2') LIMIT 1")
expect(client).to receive(:query).with("SELECT Id, Title__c FROM Post__c WHERE (Id = '2') LIMIT 1")
comment.post
Comment.belongs_to :post # reset association to original value
end
Expand Down
18 changes: 0 additions & 18 deletions spec/active_force/query_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,4 @@
expect(query.where("name = 'cool'").count.to_s).to eq "SELECT count(Id) FROM table_name WHERE (name = 'cool')"
end
end

describe '.options' do
it 'should add a where if the option has a where condition' do
expect(query.options(where: 'var = 1').to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (var = 1)"
end

it 'should add a limit if the option has a limit condition' do
expect(query.options(limit: 1).to_s).to eq "SELECT Id, name, etc FROM table_name LIMIT 1"
end

it 'should add a order if the option has a order condition' do
expect(query.options(order: 'name desc').to_s).to eq "SELECT Id, name, etc FROM table_name ORDER BY name desc"
end

it 'should work with multiples options' do
expect(query.options(where: 'var = 1', order: 'name desc', limit: 1).to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (var = 1) ORDER BY name desc LIMIT 1"
end
end
end
2 changes: 1 addition & 1 deletion spec/active_force/sobject/includes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ module ActiveForce
context 'when the relationship table name is different from the actual table name' do
it 'formulates the correct SOQL' do
soql = Salesforce::Opportunity.includes(:owner).where(id: '123').to_s
expect(soql).to eq "SELECT Id, OwnerId, AccountId, Owner.Id FROM Opportunity WHERE (Id = '123')"
expect(soql).to eq "SELECT Id, OwnerId, AccountId, Business_Partner__c, Owner.Id FROM Opportunity WHERE (Id = '123')"
end

it "queries the API once to retrieve the object and its related one" do
Expand Down
Loading

0 comments on commit ec6a53d

Please sign in to comment.