openstax_active_force 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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.mailmap +3 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.md +98 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +174 -0
  10. data/Rakefile +6 -0
  11. data/active_force.gemspec +30 -0
  12. data/lib/active_attr/dirty.rb +33 -0
  13. data/lib/active_force/active_query.rb +155 -0
  14. data/lib/active_force/association/association.rb +50 -0
  15. data/lib/active_force/association/belongs_to_association.rb +26 -0
  16. data/lib/active_force/association/eager_load_projection_builder.rb +60 -0
  17. data/lib/active_force/association/has_many_association.rb +33 -0
  18. data/lib/active_force/association/relation_model_builder.rb +70 -0
  19. data/lib/active_force/association.rb +28 -0
  20. data/lib/active_force/attribute.rb +30 -0
  21. data/lib/active_force/mapping.rb +78 -0
  22. data/lib/active_force/query.rb +110 -0
  23. data/lib/active_force/sobject.rb +210 -0
  24. data/lib/active_force/standard_types.rb +357 -0
  25. data/lib/active_force/table.rb +37 -0
  26. data/lib/active_force/version.rb +3 -0
  27. data/lib/active_force.rb +13 -0
  28. data/lib/generators/active_force/model/USAGE +8 -0
  29. data/lib/generators/active_force/model/model_generator.rb +62 -0
  30. data/lib/generators/active_force/model/templates/model.rb.erb +5 -0
  31. data/spec/active_force/active_query_spec.rb +178 -0
  32. data/spec/active_force/association/relation_model_builder_spec.rb +62 -0
  33. data/spec/active_force/association_spec.rb +157 -0
  34. data/spec/active_force/attribute_spec.rb +27 -0
  35. data/spec/active_force/callbacks_spec.rb +20 -0
  36. data/spec/active_force/mapping_spec.rb +18 -0
  37. data/spec/active_force/query_spec.rb +126 -0
  38. data/spec/active_force/sobject/includes_spec.rb +290 -0
  39. data/spec/active_force/sobject/table_name_spec.rb +27 -0
  40. data/spec/active_force/sobject_spec.rb +398 -0
  41. data/spec/active_force/table_spec.rb +25 -0
  42. data/spec/active_force_spec.rb +7 -0
  43. data/spec/fixtures/sobject/single_sobject_hash.yml +26 -0
  44. data/spec/spec_helper.rb +16 -0
  45. data/spec/support/fixture_helpers.rb +45 -0
  46. data/spec/support/restforce_factories.rb +9 -0
  47. data/spec/support/sobjects.rb +97 -0
  48. data/spec/support/whizbang.rb +30 -0
  49. metadata +196 -0
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module ActiveForce
4
+ module Association
5
+ describe RelationModelBuilder do
6
+ let(:instance){ described_class.new association, value }
7
+
8
+ describe '#build_relation_model' do
9
+ context 'has_many' do
10
+ let(:association){ HasManyAssociation.new Post, :comments }
11
+
12
+ context 'with values' do
13
+ let(:value) do
14
+ build_restforce_collection([
15
+ Restforce::SObject.new({'Id' => '213', 'PostId' => '123'}),
16
+ Restforce::SObject.new({'Id' => '214', 'PostId' => '123'})
17
+ ])
18
+ end
19
+
20
+ it 'returns an array of Comments' do
21
+ comments = instance.build_relation_model
22
+ expect(comments).to be_a Array
23
+ expect(comments.all?{ |c| c.is_a? Comment }).to be true
24
+ end
25
+ end
26
+
27
+ context 'without values' do
28
+ let(:value){ nil }
29
+
30
+ it 'returns an empty array' do
31
+ comments = instance.build_relation_model
32
+ expect(comments).to be_a Array
33
+ expect(comments).to be_empty
34
+ end
35
+ end
36
+ end
37
+
38
+ context 'belongs_to' do
39
+ let(:association){ BelongsToAssociation.new(Comment, :post) }
40
+
41
+ context 'with a value' do
42
+ let(:value) do
43
+ build_restforce_sobject 'Id' => '213'
44
+ end
45
+
46
+ it 'returns a post' do
47
+ expect(instance.build_relation_model).to be_a Post
48
+ end
49
+ end
50
+
51
+ context 'without a value' do
52
+ let(:value){ nil }
53
+
54
+ it 'returns nil' do
55
+ expect(instance.build_relation_model).to be_nil
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce::SObject do
4
+ let :post do
5
+ Post.new(id: "1", title: 'Ham')
6
+ end
7
+
8
+ let :comment do
9
+ Comment.new(id: "1", post_id: "1")
10
+ end
11
+
12
+ let :client do
13
+ double("sfdc_client", query: [Restforce::Mash.new("Id" => 1)])
14
+ end
15
+
16
+ before do
17
+ ActiveForce.sfdc_client = client
18
+ end
19
+
20
+ describe "has_many_query" do
21
+ it "should respond to relation method" do
22
+ expect(post).to respond_to(:comments)
23
+ end
24
+
25
+ it "should return a ActiveQuery object" do
26
+ expect(post.comments).to be_a ActiveForce::ActiveQuery
27
+ end
28
+
29
+ it 'makes only one API call to fetch the associated object' do
30
+ expect(client).to receive(:query).once
31
+ post.comments.to_a
32
+ post.comments.to_a
33
+ end
34
+
35
+ describe 'to_s' do
36
+ it "should return a SOQL statment" do
37
+ soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PostId = '1')"
38
+ expect(post.comments.to_s).to eq soql
39
+ end
40
+ end
41
+
42
+ context 'when the SObject is namespaced' do
43
+ let(:account){ Foo::Account.new(id: '1') }
44
+
45
+ it 'correctly infers the foreign key and forms the correct query' do
46
+ soql = "SELECT Id, AccountId, Partner_Account_Id__c FROM Opportunity WHERE (AccountId = '1')"
47
+ expect(account.opportunities.to_s).to eq soql
48
+ end
49
+
50
+ it 'uses an explicit foreign key if it is supplied' do
51
+ soql = "SELECT Id, AccountId, Partner_Account_Id__c FROM Opportunity WHERE (Partner_Account_Id__c = '1')"
52
+ expect(account.partner_opportunities.to_s).to eq soql
53
+ end
54
+ end
55
+ end
56
+
57
+ describe 'has_many(options)' do
58
+ it 'should allow to send a different query table name' do
59
+ soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PostId = '1')"
60
+ expect(post.ugly_comments.to_s).to eq soql
61
+ end
62
+
63
+ it 'should allow to change the foreign key' do
64
+ soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PosterId__c = '1')"
65
+ expect(post.poster_comments.to_s).to eq soql
66
+ end
67
+
68
+ it 'should allow to add a where condition' do
69
+ soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (1 = 0) AND (PostId = '1')"
70
+ expect(post.impossible_comments.to_s).to eq soql
71
+ end
72
+
73
+ it 'accepts custom scoping' do
74
+ soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (Body__c = 'RE: Ham') AND (PostId = '1') ORDER BY CreationDate DESC"
75
+ expect(post.reply_comments.to_s).to eq soql
76
+ end
77
+
78
+ it 'accepts custom scoping that preloads associations of the association' do
79
+ account = Salesforce::Account.new id: '1', business_partner: 'qwerty'
80
+ soql = "SELECT Id, OwnerId, AccountId, Business_Partner__c, Owner.Id FROM Opportunity WHERE (Business_Partner__c = 'qwerty') AND (AccountId = '1')"
81
+ expect(account.partner_opportunities.to_s).to eq soql
82
+ end
83
+
84
+ it 'should use a convention name for the foreign key' do
85
+ soql = "SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__c WHERE (PostId = '1')"
86
+ expect(post.comments.to_s).to eq soql
87
+ end
88
+ end
89
+
90
+ describe "belongs_to" do
91
+ it "should get the resource it belongs to" do
92
+ expect(comment.post).to be_instance_of(Post)
93
+ end
94
+
95
+ it "should allow to pass a foreign key as options" do
96
+ Comment.belongs_to :post, foreign_key: :fancy_post_id
97
+ allow(comment).to receive(:fancy_post_id).and_return "2"
98
+ expect(client).to receive(:query).with("SELECT Id, Title__c FROM Post__c WHERE (Id = '2') LIMIT 1")
99
+ comment.post
100
+ Comment.belongs_to :post # reset association to original value
101
+ end
102
+
103
+ it 'makes only one API call to fetch the associated object' do
104
+ expect(client).to receive(:query).once
105
+ comment.post
106
+ comment.post
107
+ end
108
+
109
+ describe "assignments" do
110
+ let(:comment) do
111
+ comment = Comment.new(id: '1')
112
+ comment.post = Post.new(id: '1')
113
+ comment
114
+ end
115
+
116
+ before do
117
+ expect(client).to_not receive(:query)
118
+ end
119
+
120
+ it 'accepts assignment of an existing object as an association' do
121
+ expect(client).to_not receive(:query)
122
+ other_post = Post.new(id: "2")
123
+ comment.post = other_post
124
+ expect(comment.post_id).to eq other_post.id
125
+ expect(comment.post).to eq other_post
126
+ end
127
+
128
+ it 'can desassociate an object by setting it as nil' do
129
+ comment.post = nil
130
+ expect(comment.post_id).to eq nil
131
+ expect(comment.post).to eq nil
132
+ end
133
+ end
134
+
135
+ context 'when the SObject is namespaced' do
136
+ let(:attachment){ Foo::Attachment.new(id: '1', lead_id: '2') }
137
+
138
+ it 'generates the correct query' do
139
+ expect(client).to receive(:query).with("SELECT Id FROM Lead WHERE (Id = '2') LIMIT 1")
140
+ attachment.lead
141
+ end
142
+
143
+ it 'instantiates the correct object' do
144
+ expect(attachment.lead).to be_instance_of(Foo::Lead)
145
+ end
146
+
147
+ context 'when given a foreign key' do
148
+ let(:attachment){ Foo::Attachment.new(id: '1', fancy_lead_id: '2') }
149
+
150
+ it 'generates the correct query' do
151
+ expect(client).to receive(:query).with("SELECT Id FROM Lead WHERE (Id = '2') LIMIT 1")
152
+ attachment.fancy_lead
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce::Attribute do
4
+ let(:attribute) { ActiveForce::Attribute }
5
+
6
+ describe 'initialize' do
7
+ let(:some_field) { attribute.new(:some_field) }
8
+
9
+ it 'should set "from" and "as" as default' do
10
+ expect(some_field.sfdc_name).to eq 'Some_Field__c'
11
+ expect(some_field.as).to eq :string
12
+ end
13
+
14
+ it 'should take values from the option parameter' do
15
+ other_field = attribute.new(:other_field, sfdc_name: 'OT__c', as: :integer)
16
+ expect(other_field.sfdc_name).to eq 'OT__c'
17
+ expect(other_field.as).to eq :integer
18
+ end
19
+ end
20
+
21
+ describe 'when the attribute is' do
22
+ it 'a multipick should return all values as 1 string separated with ";"' do
23
+ names = attribute.new(:names, as: :multi_picklist)
24
+ expect(names.value_for_hash ['olvap', 'eloy']).to eq 'olvap;eloy'
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce::SObject do
4
+ let(:client) { double 'Client', create!: 'id' }
5
+
6
+ before do
7
+ ActiveForce.sfdc_client = client
8
+ end
9
+
10
+ describe "save" do
11
+ it 'call action callback when save a record' do
12
+ whizbanged = Whizbang.new
13
+ whizbanged.save
14
+ expect(whizbanged.updated_from).to eq 'Rails'
15
+ expect(whizbanged.dirty_attribute).to eq true
16
+ expect(whizbanged.changed.include? 'dirty_attribute').to eq true
17
+ expect(whizbanged.changed.include? 'updated_from').to eq false
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce::Mapping do
4
+ let(:mapping){ ActiveForce::Mapping.new 'some_table' }
5
+
6
+ describe 'field' do
7
+ it 'should add a new attribute to the instance' do
8
+ mapping.field :id, from: 'Id'
9
+ expect(mapping.mappings).to eq({ id: 'Id' })
10
+ end
11
+
12
+ it 'sf_names should return all attributes names from salesforce' do
13
+ mapping.field :id, from: 'Id'
14
+ mapping.field :name, from: 'Name'
15
+ expect(mapping.sfdc_names).to eq ['Id', 'Name']
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce::Query do
4
+ let(:query) do
5
+ query = ActiveForce::Query.new 'table_name'
6
+ query.fields ['name', 'etc']
7
+ query
8
+ end
9
+
10
+ describe '.select' do
11
+ it 'use column sent on the select method' do
12
+ expect(query.select('name').all.to_s).to eq "SELECT name FROM table_name"
13
+ end
14
+
15
+ it 'use columns sent on the select method' do
16
+ expect(query.select(['id','name']).all.to_s).to eq "SELECT id, name FROM table_name"
17
+ end
18
+ end
19
+
20
+ describe ".all" do
21
+ it "table should return table name" do
22
+ expect(query.all.table).to eq(query.table)
23
+ end
24
+
25
+ it "fields should return fields" do
26
+ expect(query.all.fields).to eq query.fields
27
+ end
28
+ end
29
+
30
+ describe ".all.to_s" do
31
+ it "should return a query for all records" do
32
+ expect(query.all.to_s).to eq "SELECT Id, name, etc FROM table_name"
33
+ end
34
+
35
+ it "should ignore dupicated attributes in select statment" do
36
+ query.fields ['Id', 'name', 'etc']
37
+ expect(query.all.to_s).to eq "SELECT Id, name, etc FROM table_name"
38
+ end
39
+ end
40
+
41
+ describe ".where" do
42
+ it "should add a where condition to a query" do
43
+ expect(query.where("name like '%a%'").to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (name like '%a%')"
44
+ end
45
+
46
+ it "should add multiples conditions to a query with parentheses" do
47
+ expect(query.where("condition1 = 1").where("condition2 = 2 OR condition3 = 3").to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (condition1 = 1) AND (condition2 = 2 OR condition3 = 3)"
48
+ end
49
+ end
50
+
51
+ describe ".limit" do
52
+ it "should add a limit to a query" do
53
+ expect(query.limit("25").to_s).to eq "SELECT Id, name, etc FROM table_name LIMIT 25"
54
+ end
55
+ end
56
+
57
+ describe ".limit_value" do
58
+ it "should return the limit value" do
59
+ query.limit(4)
60
+ expect(query.limit_value).to eq 4
61
+ end
62
+ end
63
+
64
+ describe ".offset" do
65
+ it "should add an offset to a query" do
66
+ expect(query.offset(4).to_s).to eq "SELECT Id, name, etc FROM table_name OFFSET 4"
67
+ end
68
+ end
69
+
70
+ describe ".offset_value" do
71
+ it "should return the offset value" do
72
+ query.offset(4)
73
+ expect(query.offset_value).to eq 4
74
+ end
75
+ end
76
+
77
+ describe ".find.to_s" do
78
+ it "should return a query for 1 record" do
79
+ expect(query.find(2).to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (Id = '2') LIMIT 1"
80
+ end
81
+ end
82
+
83
+ describe ".order" do
84
+ it "should add a order condition in the statment" do
85
+ expect(query.order("name desc").to_s).to eq "SELECT Id, name, etc FROM table_name ORDER BY name desc"
86
+ end
87
+
88
+ it "should add a order condition in the statment with WHERE and LIMIT" do
89
+ expect(query.where("condition1 = 1").order("name desc").limit(1).to_s).to eq "SELECT Id, name, etc FROM table_name WHERE (condition1 = 1) ORDER BY name desc LIMIT 1"
90
+ end
91
+ end
92
+
93
+ describe '.join' do
94
+ let(:join_query) do
95
+ join = ActiveForce::Query.new 'join_table_name'
96
+ join.fields ['name', 'etc']
97
+ join
98
+ end
99
+
100
+ it 'should add another select statment on the current select' do
101
+ expect(query.join(join_query).to_s).to eq 'SELECT Id, name, etc, (SELECT Id, name, etc FROM join_table_name) FROM table_name'
102
+ end
103
+ end
104
+
105
+ describe '.first' do
106
+ it 'should return the query for the first record' do
107
+ expect(query.first.to_s).to eq 'SELECT Id, name, etc FROM table_name LIMIT 1'
108
+ end
109
+ end
110
+
111
+ describe '.last' do
112
+ it 'should return the query for the last record' do
113
+ expect(query.last.to_s).to eq 'SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT 1'
114
+ end
115
+ end
116
+
117
+ describe ".count" do
118
+ it "should return the query for getting the row count" do
119
+ expect(query.count.to_s).to eq 'SELECT count(Id) FROM table_name'
120
+ end
121
+
122
+ it "should work with a condition" do
123
+ expect(query.where("name = 'cool'").count.to_s).to eq "SELECT count(Id) FROM table_name WHERE (name = 'cool')"
124
+ end
125
+ end
126
+ end