active_force 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2db05b88b32eacda2b94f3dcbc47a72288b1f4e4
4
- data.tar.gz: 4c57fa4fa03fcb50d8378854b99377d6403206e2
3
+ metadata.gz: 5fc8bc2425de35aa52c303eb28a9165a2601c5f5
4
+ data.tar.gz: b397002aa1906d23b92d4d20ff08745d349c6cb4
5
5
  SHA512:
6
- metadata.gz: 7629ee051094c5e4dbe73ee6c3c06fb31bbb1fa9cf5aea5705f661d5201dffa8eb9cc6d5fcc263e7ea90084f4bf30731749d206f2a8996a9c356cd876e4415ba
7
- data.tar.gz: 21db5698eb4a8f6db16041cc70cd32a8d24520dc1655c7c6eb6bb3d2d1fa7d3b5e44c99e5ffbe93860c9bff2a19872edd5ceff8a20cdb8ee9d1512d103133223
6
+ metadata.gz: ef355901a29bd4ff95bf593400f3a52e32e53cd4bec886b5d057984a31092f832b0ce0cf4608462f6379652ad91120d4437100177d4f3912e0051a85abbfa9fb
7
+ data.tar.gz: 8ce4758557b84eef8662c8f53ff3e301ecdc077d1f824ef2850cb17636d6dde5cab9ed564ca57d3b02ed9a600a188b6e133ebb6eefa8f536803d3a767abcf672
data/CHANGELOG.md CHANGED
@@ -1,11 +1,70 @@
1
- # 0.0.7.alfa #
1
+ # Changelog
2
2
 
3
- * Use simpler error messages.
4
- * Implement .all and .create methods (copying ActiveRecord Interface)
5
- * Add SObject.all method.
6
- * Add basic support for multi-picklists.
3
+ ## Not released
7
4
 
8
- # 0.0.6.alfa #
5
+ ## 0.5.0
6
+
7
+ * Provide a default id field for all SObject subclassees ([Dan Olson][], [#30][])
8
+ * Fix Ruby 2.0 compatibility issue ([Dan Olson][], [Pablo Oldani][], [#28][])
9
+ * Normalize rspec syntax to remove deprecation warnings ([Dan Olson][], [#26][])
10
+ * Remove namespace when inferring default SObject.table_name ([Dan Olson][], [#24][])
11
+ * Add create! and save! methods. ([Pablo Oldani][], [#21][])
12
+ * Refactor update and create methods. ([Pablo Oldani][], [#21][])
13
+ * Add a generator. ([José Piccioni][], [#19][])
14
+ * ActiveQuery now provides :each, :map and :inspect. ([Armando Andini][])
15
+ * Add SObject.create class mehtod. ([Pablo Oldani][], [#10][])
16
+ * SObject.field default mapping value follows SFDC API naming convention.
17
+ ([Dan Olson][], [#14][] [#15][])
18
+
19
+ ## 0.4.2
20
+
21
+ * Use ActiveQuery instead of Query. ([Armando Andini][])
22
+ * Add instructions to use validations ([José Piccioni][])
23
+ * Lots of refactoring.
24
+
25
+ ## 0.3.2
26
+
27
+ * Fixed gemspec.
28
+
29
+ ## 0.3.1
30
+
31
+ * Create different classes for associations. ([#4][])
32
+ * Big refactor on has_many association. ([Armando Andini][])
33
+ * Add a lot of specs and refactors. ([Armando Andini][])
34
+ * Add a Finders module. ([Armando Andini][])
35
+ * Add fist and last method to SObject.
36
+
37
+ ## 0.2.0
38
+
39
+ * Add belogns_to and has_many associations.
40
+ * Changed when the SOQL query is sent to the client.
41
+ * Add join method to query to use associtations.
42
+
43
+ ## 0.1.0
44
+
45
+ * Add query builder object to chain conditions.
46
+ * Update update and create methods.
47
+ * Add Campaing standard table name.
48
+
49
+ ## 0.0.6.alfa
9
50
 
10
51
  * ActiveForce::SObject#table_name is auto populated using the class
11
52
  name. It adds "__c" to all non standard types.
53
+
54
+ <!--- The following link definition list is generated by PimpMyChangelog --->
55
+
56
+ [#4]: https://github.com/ionia-corporation/active_force/issues/4
57
+ [#9]: https://github.com/ionia-corporation/active_force/issues/9
58
+ [#10]: https://github.com/ionia-corporation/active_force/issues/10
59
+ [#14]: https://github.com/ionia-corporation/active_force/issues/14
60
+ [#15]: https://github.com/ionia-corporation/active_force/issues/15
61
+ [#19]: https://github.com/ionia-corporation/active_force/issues/19
62
+ [#21]: https://github.com/ionia-corporation/active_force/issues/21
63
+ [#24]: https://github.com/ionia-corporation/active_force/issues/24
64
+ [#26]: https://github.com/ionia-corporation/active_force/issues/26
65
+ [#28]: https://github.com/ionia-corporation/active_force/issues/28
66
+ [#30]: https://github.com/ionia-corporation/active_force/issues/30
67
+ [Pablo Oldani]: https://github.com/olvap
68
+ [Armando Andini]: https://github.com/antico5
69
+ [José Piccioni]: https://github.com/lmhsjackson
70
+ [Dan Olson]: https://github.com/DanOlson
data/README.md CHANGED
@@ -7,8 +7,11 @@
7
7
 
8
8
  # ActiveForce
9
9
 
10
- A ruby gem to interact with SalesForce as if it were Active Record. It
11
- uses Restforce to interact with the API, so it is fast and stable.
10
+ A ruby gem to interact with [SalesForce][1] as if it were Active Record. It
11
+ uses [Restforce][2] to interact with the API, so it is fast and stable.
12
+
13
+ [1]: http://www.salesforce.com
14
+ [2]: https://github.com/ejholmes/restforce
12
15
 
13
16
  ## Installation
14
17
 
@@ -43,17 +46,19 @@ end
43
46
  ```
44
47
 
45
48
  ### Add Attributes
49
+
46
50
  ```ruby
47
51
  class Page < ActiveForce::SObject
48
52
  #field, :attribute_name, from: 'Name_In_Salesforce_Database'
49
53
  field :id, from: 'Id'
50
54
  field :name, from: 'Medication__c'
51
- self.fields = mappings.values
52
55
  #set SalesForce table name.
53
56
  self.table_name = 'Patient_Medication__c'
54
57
  end
55
58
  ```
59
+
56
60
  ### Validations
61
+
57
62
  You can use any validation that active record has (except for validates_associated), just by adding them to your class:
58
63
 
59
64
  ```ruby
@@ -96,6 +101,7 @@ end
96
101
  ```
97
102
 
98
103
  #### Belongs to
104
+
99
105
  ```ruby
100
106
  class Account < ActiveForce::SObject
101
107
  end
@@ -112,4 +118,6 @@ end
112
118
  2. Create your feature branch (`git checkout -b my-new-feature`)
113
119
  3. Commit your changes (`git commit -am 'Add some feature'`)
114
120
  4. Push to the branch (`git push origin my-new-feature`)
115
- 5. Create new Pull Request
121
+ 5. Create new Pull Request so we can talk about it.
122
+ 6. Once accepted, please add an entry in the CHANGELOG and rebase your changes
123
+ to squash typos or corrections.
@@ -1,11 +1,14 @@
1
1
  require 'active_force/query'
2
+ require 'forwardable'
2
3
 
3
4
  module ActiveForce
4
5
  class ActiveQuery < Query
6
+ extend Forwardable
5
7
 
6
8
  attr_reader :sobject
7
9
 
8
- delegate :sfdc_client, :build, :table_name, :mappings, to: :sobject
10
+ def_delegators :sobject, :sfdc_client, :build, :table_name, :mappings
11
+ def_delegators :to_a, :each, :map, :inspect
9
12
 
10
13
  def initialize sobject
11
14
  @sobject = sobject
@@ -14,20 +17,16 @@ module ActiveForce
14
17
  end
15
18
 
16
19
  def to_a
17
- result.to_a.map do |mash|
18
- build mash
19
- end
20
+ result.to_a.map { |mash| build mash }
20
21
  end
21
22
 
23
+ alias_method :all, :to_a
24
+
22
25
  def count
23
26
  super
24
27
  sfdc_client.query(to_s).first.expr0
25
28
  end
26
29
 
27
- def all
28
- to_a
29
- end
30
-
31
30
  def limit limit
32
31
  super
33
32
  limit == 1 ? to_a.first : self
@@ -15,15 +15,16 @@ module ActiveForce
15
15
  @options[:model] || @relation_name.to_s.singularize.camelcase.constantize
16
16
  end
17
17
 
18
+ def foreign_key
19
+ @options[:foreign_key] || default_foreign_key
20
+ end
21
+
18
22
  private
19
23
 
20
24
  def build
21
25
  define_relation_method
22
26
  end
23
27
 
24
- def default_sfdc_foreign_key
25
- relation_model.mappings["#{ @parent.name.downcase }_id".to_sym]
26
- end
27
28
  end
28
29
 
29
30
  end
@@ -3,16 +3,16 @@ module ActiveForce
3
3
 
4
4
  class BelongsToAssociation < Association
5
5
 
6
- def foreign_key
7
- options[:foreign_key] || "#{ @relation_name }_id".to_sym
8
- end
9
-
10
6
  private
11
7
 
8
+ def default_foreign_key
9
+ "#{ relation_model.name.downcase }_id".to_sym
10
+ end
11
+
12
12
  def define_relation_method
13
13
  association = self
14
14
  @parent.send :define_method, @relation_name do
15
- association.relation_model.find(self.send association.foreign_key)
15
+ association.relation_model.find(send association.foreign_key)
16
16
  end
17
17
  end
18
18
  end
@@ -2,18 +2,18 @@ module ActiveForce
2
2
  module Association
3
3
  class HasManyAssociation < Association
4
4
 
5
- def foreign_key
6
- @options[:foreign_key] || default_sfdc_foreign_key || @parent.table_name
7
- end
8
-
9
5
  private
10
6
 
7
+ def default_foreign_key
8
+ "#{ @parent.name.downcase }_id".to_sym
9
+ end
10
+
11
11
  def define_relation_method
12
12
  association = self
13
13
  @parent.send :define_method, @relation_name do
14
14
  query = association.relation_model.query
15
15
  query.options association.options
16
- query.where "#{ association.foreign_key } = '#{ self.id }'"
16
+ query.where association.foreign_key => self.id
17
17
  end
18
18
  end
19
19
  end
@@ -4,25 +4,59 @@ require 'active_attr/dirty'
4
4
  require 'active_force/active_query'
5
5
  require 'active_force/association'
6
6
  require 'yaml'
7
+ require 'forwardable'
8
+ require 'logger'
9
+
7
10
 
8
11
  module ActiveForce
9
12
  class SObject
10
13
  include ActiveAttr::Model
11
14
  include ActiveAttr::Dirty
12
15
  include ActiveForce::Association
13
-
14
16
  STANDARD_TYPES = %w[ Account Contact Opportunity Campaign]
15
17
 
16
- class_attribute :mappings, :fields, :table_name
18
+ class_attribute :mappings, :table_name
17
19
 
18
20
  class << self
19
- delegate :where, :first, :last, :all, :find, :find_by, :count, :to => :query
21
+ extend Forwardable
22
+ def_delegators :query, :where, :first, :last, :all, :find, :find_by, :count
23
+
24
+ private
25
+
26
+ ###
27
+ # Transforms +attribute+ to the conventional Salesforce API name.
28
+ #
29
+ # Example:
30
+ # > default_api_name :some_attribute
31
+ # => "Some_Attribute__c"
32
+ def default_api_name(attribute)
33
+ String(attribute).split('_').map(&:capitalize).join('_') << '__c'
34
+ end
35
+
36
+ def custom_table_name
37
+ self.name if STANDARD_TYPES.include? self.name
38
+ end
39
+
40
+ ###
41
+ # Provide each subclass with a default id field. Can be overridden
42
+ # in the subclass if needed
43
+ def inherited(subclass)
44
+ subclass.field :id, from: 'Id'
45
+ end
20
46
  end
21
47
 
22
48
  # The table name to used to make queries.
23
49
  # It is derived from the class name adding the "__c" when needed.
24
50
  def self.table_name
25
- @table_name ||= custom_table_name || "#{ self.name }__c"
51
+ @table_name ||= custom_table_name || "#{ self.name.split('::').last }__c"
52
+ end
53
+
54
+ def self.fields
55
+ mappings.values
56
+ end
57
+
58
+ def self.query
59
+ ActiveForce::ActiveQuery.new self
26
60
  end
27
61
 
28
62
  def self.build sf_table_description
@@ -35,53 +69,41 @@ module ActiveForce
35
69
  sobject
36
70
  end
37
71
 
38
- def self.query
39
- ActiveForce::ActiveQuery.new self
40
- end
41
-
42
72
  def update_attributes! attributes = {}
43
73
  assign_attributes attributes
44
74
  return false unless valid?
45
- sobject_hash = { 'Id' => id }
46
- changed.each do |field|
47
- sobject_hash[mappings[field.to_sym]] = read_attribute(field)
48
- end
49
- result = sfdc_client.update! table_name, sobject_hash
75
+ sfdc_client.update! table_name, attributes_for_sfdb_update
50
76
  changed_attributes.clear
51
- result
77
+ self
52
78
  end
53
79
 
54
80
  def update_attributes attributes = {}
55
81
  update_attributes! attributes
56
82
  rescue Faraday::Error::ClientError => error
57
- Rails.logger.info do
58
- "[SFDC] [#{self.class.model_name}] [#{self.class.table_name}] Error while updating, params: #{hash}, error: #{error.inspect}"
59
- end
60
- errors[:base] << error.message
61
- false
83
+ logger_output __method__
62
84
  end
63
85
 
64
86
  alias_method :update, :update_attributes
65
87
 
66
88
  def create!
67
89
  return false unless valid?
68
- hash = {}
69
- mappings.map do |field, name_in_sfdc|
70
- value = read_value field
71
- hash[name_in_sfdc] = value if value.present?
72
- end
73
- self.id = sfdc_client.create! table_name, hash
90
+ self.id = sfdc_client.create! table_name, attributes_for_sfdb_create
74
91
  changed_attributes.clear
92
+ self
75
93
  end
76
94
 
77
95
  def create
78
96
  create!
79
97
  rescue Faraday::Error::ClientError => error
80
- Rails.logger.info do
81
- "[SFDC] [#{self.class.model_name}] [#{self.class.table_name}] Error while creating, params: #{hash}, error: #{error.inspect}"
82
- end
83
- errors[:base] << error.message
84
- false
98
+ logger_output __method__
99
+ end
100
+
101
+ def self.create args
102
+ new(args).save
103
+ end
104
+
105
+ def self.create! args
106
+ new(args).save!
85
107
  end
86
108
 
87
109
  def save
@@ -92,6 +114,14 @@ module ActiveForce
92
114
  end
93
115
  end
94
116
 
117
+ def save!
118
+ if persisted?
119
+ update_attributes!
120
+ else
121
+ create!
122
+ end
123
+ end
124
+
95
125
  def to_param
96
126
  id
97
127
  end
@@ -100,43 +130,72 @@ module ActiveForce
100
130
  id?
101
131
  end
102
132
 
103
- def self.field field_name, from: field_name.camelize, as: :string
104
- mappings[field_name] = from
105
- attribute field_name, sf_type: as
133
+ def self.field field_name, args = {}
134
+ args[:from] ||= default_api_name(field_name)
135
+ args[:as] ||= :string
136
+ mappings[field_name] = args[:from]
137
+ attribute field_name, sf_type: args[:as]
106
138
  end
107
139
 
108
140
  def self.mappings
109
141
  @mappings ||= {}
110
142
  end
111
143
 
112
-
113
144
  private
114
145
 
115
- def self.custom_table_name
116
- self.name if STANDARD_TYPES.include? self.name
146
+ def logger_output action
147
+ logger = Logger.new(STDOUT)
148
+ logger.info("[SFDC] [#{self.class.model_name}] [#{self.class.table_name}] Error while #{ action }, params: #{hash}, error: #{error.inspect}")
149
+ errors[:base] << error.message
150
+ false
117
151
  end
118
152
 
119
- def read_value field
120
- if self.class.attributes[field][:sf_type] == :multi_picklist
121
- attribute(field.to_s).reject(&:empty?).join(';')
122
- else
123
- attribute(field.to_s)
124
- end
153
+ def attributes_for_sfdb_create
154
+ attrs = mappings.map do |attr, sf_field|
155
+ value = read_attribute(attr)
156
+ [sf_field, value] if value
125
157
  end
158
+ Hash.new(attrs.compact)
159
+ end
160
+
126
161
 
127
- def self.picklist field
128
- picks = sfdc_client.picklist_values(table_name, mappings[field])
129
- picks.map do |value|
130
- [value[:label], value[:value]]
131
- end
162
+ def attributes_for_sfdb_update
163
+ attrs = changed_mappings.map do |attr, sf_field|
164
+ [sf_field, read_attribute(attr)]
132
165
  end
166
+ Hash.new(attrs).merge('Id' => id)
167
+ end
168
+
169
+ def changed_mappings
170
+ mappings.select { |attr, sf_field| changed.include? attr.to_s}
171
+ end
133
172
 
134
- def self.sfdc_client
135
- Client
173
+ def read_value field
174
+ case sf_field_type field
175
+ when :multi_picklist
176
+ attribute(field.to_s).reject(&:empty?).join(';')
177
+ else
178
+ attribute(field.to_s)
136
179
  end
180
+ end
181
+
182
+ def sf_field_type field
183
+ self.class.attributes[field][:sf_tpye]
184
+ end
137
185
 
138
- def sfdc_client
139
- self.class.sfdc_client
186
+ def self.picklist field
187
+ picks = sfdc_client.picklist_values(table_name, mappings[field])
188
+ picks.map do |value|
189
+ [value[:label], value[:value]]
140
190
  end
191
+ end
192
+
193
+ def self.sfdc_client
194
+ @client ||= Restforce.new
195
+ end
196
+
197
+ def sfdc_client
198
+ self.class.sfdc_client
199
+ end
141
200
  end
142
201
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveForce
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -1,22 +1,47 @@
1
- class ActiveForce::ActiveForceModelGenerator < Rails::Generators::NamedBase
1
+ module ActiveForce
2
+ class ActiveForceModelGenerator < Rails::Generators::NamedBase
2
3
 
3
- Attribute = Struct.new(:local_name, :remote_name)
4
+ source_root File.expand_path('../templates', __FILE__)
4
5
 
5
- source_root File.expand_path('../templates', __FILE__)
6
- argument :attributes, type: :array, default: [],
7
- banner: "field[:sales_force_name] field[:sales_force_name]"
6
+ def create_model_file
8
7
 
9
- def create_model_file
10
- template "model.rb.erb", "app/models/#{file_name}.rb"
11
- end
8
+ sf_field_names = list_field_names file_name.capitalize
9
+ @attributes = create_attributes sf_field_names
10
+
11
+ template "model.rb.erb", "app/models/#{file_name}.rb"
12
+ end
12
13
 
13
- protected
14
+ protected
15
+
16
+ Attribute = Struct.new(:local_name, :remote_name)
17
+
18
+ def create_attributes sf_field_names
19
+ sf_field_names.map do |field|
20
+ Attribute.new( sf_name_to_symbol(field) ,field)
21
+ end
22
+ end
14
23
 
15
- def parse_attributes! #:nodoc:
16
- self.attributes = (attributes || []).map do |attr|
17
- name, sales_force_name = attr.split ':', 2
18
- remote_name = sales_force_name.presence || name
19
- Attribute.new name, remote_name
24
+ def list_field_names table_name
25
+ Client.describe(table_name).fields.map do |field|
26
+ field.name
27
+ end
20
28
  end
29
+
30
+ def sf_name_to_symbol sf_name
31
+ sf_name = sf_name.underscore
32
+ sf_name = sf_name[0..-4] if sf_name.include? "__c"
33
+ sf_name.to_sym
34
+ end
35
+
36
+ class String
37
+ def underscore
38
+ self.gsub(/::/, '/').
39
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
40
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
41
+ tr("-", "_").
42
+ downcase
43
+ end
44
+ end
45
+
21
46
  end
22
- end
47
+ end
@@ -2,21 +2,10 @@ require 'active_force/sobject'
2
2
 
3
3
  class <%= class_name %> < ActiveForce::SObject
4
4
 
5
- MAPPINGS = {
6
- <% attributes.each do |attribute| -%>
7
- <%= attribute.local_name %>: '<%= attribute.remote_name %>',
5
+ <% @attributes.each do |attribute| -%>
6
+ field :<%= attribute.local_name %>, from: '<%= attribute.remote_name %>',
8
7
  <% end -%>
9
- id: 'Id'
10
- }
11
- FIELDS = MAPPINGS.values
12
- RELATED_LIST_FIELDS = FIELDS
13
8
 
14
- self.mappings = MAPPINGS
15
- self.fields = FIELDS
16
9
  self.table_name = '<%= class_name %>'
17
10
 
18
- <% attributes.each do |attribute| -%>
19
- attribute :<%= attribute.local_name %>
20
- <% end -%>
21
-
22
11
  end
@@ -4,8 +4,9 @@ require 'active_force/active_query'
4
4
  describe ActiveForce::ActiveQuery do
5
5
  let(:sobject){
6
6
  sobject = double("sobject")
7
- sobject.stub(:table_name).and_return "table_name"
8
- sobject.stub(:fields).and_return []
7
+ allow(sobject).to receive(:table_name).and_return "table_name"
8
+ allow(sobject).to receive(:fields).and_return []
9
+ allow(sobject).to receive(:mappings).and_return({field: "Field__c"})
9
10
  sobject
10
11
  }
11
12
 
@@ -13,10 +14,11 @@ describe ActiveForce::ActiveQuery do
13
14
  double("client")
14
15
  }
15
16
 
17
+ let(:active_query){ ActiveForce::ActiveQuery.new(sobject) }
18
+
16
19
  before do
17
- @active_query = ActiveForce::ActiveQuery.new(sobject)
18
- @active_query.stub(:sfdc_client).and_return client
19
- @active_query.stub(:build).and_return Object.new
20
+ allow(active_query).to receive(:sfdc_client).and_return client
21
+ allow(active_query).to receive(:build).and_return Object.new
20
22
  end
21
23
 
22
24
  describe "to_a" do
@@ -25,14 +27,47 @@ describe ActiveForce::ActiveQuery do
25
27
  end
26
28
 
27
29
  it "should return an array of objects" do
28
- result = @active_query.where("Text_Label = 'foo'").to_a
30
+ result = active_query.where("Text_Label = 'foo'").to_a
29
31
  expect(result).to be_a Array
30
32
  end
31
33
 
32
34
  it "should allow to chain query methods" do
33
- result = @active_query.where("Text_Label = 'foo'").where("Checkbox_Label = true").to_a
35
+ result = active_query.where("Text_Label = 'foo'").where("Checkbox_Label = true").to_a
34
36
  expect(result).to be_a Array
35
37
  end
38
+ end
39
+
40
+ describe "condition mapping" do
41
+ it "maps conditions for a .where" do
42
+ active_query.where(field: 123)
43
+ expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE Field__c = 123")
44
+ end
45
+
46
+ it "encloses the value in quotes if it's a string" do
47
+ active_query.where field: "hello"
48
+ expect(active_query.to_s).to end_with("Field__c = 'hello'")
49
+ end
50
+ end
51
+
52
+ describe "#find_by" do
53
+ it "should query the client, with the SFDC field names and correctly enclosed values" do
54
+ expect(client).to receive :query
55
+ active_query.find_by field: 123
56
+ expect(active_query.to_s).to eq "SELECT Id FROM table_name WHERE Field__c = 123 LIMIT 1"
57
+ end
58
+ end
59
+
60
+ describe "responding as an enumerable" do
61
+ before do
62
+ expect(active_query).to receive(:to_a).and_return([])
63
+ end
64
+
65
+ it "should call to_a when receiving each" do
66
+ active_query.each {}
67
+ end
36
68
 
69
+ it "should call to_a when receiving map" do
70
+ active_query.map {}
71
+ end
37
72
  end
38
73
  end
@@ -5,14 +5,14 @@ describe ActiveForce::SObject do
5
5
 
6
6
  let :post do
7
7
  post = Post.new
8
- post.stub(:id).and_return "1"
8
+ allow(post).to receive(:id).and_return "1"
9
9
  post
10
10
  end
11
11
 
12
12
  let :comment do
13
13
  comment = Comment.new
14
- comment.stub(:id).and_return "1"
15
- comment.stub(:post_id).and_return "1"
14
+ allow(comment).to receive(:id).and_return "1"
15
+ allow(comment).to receive(:post_id).and_return "1"
16
16
  comment
17
17
  end
18
18
 
@@ -26,10 +26,11 @@ describe ActiveForce::SObject do
26
26
  end
27
27
 
28
28
  class Comment < ActiveForce::SObject
29
+ field :post_id, from: "PostId"
29
30
  self.table_name = "Comment__c"
30
31
  end
31
32
 
32
- ActiveForce::SObject.stub(:sfdc_client).and_return client
33
+ allow(ActiveForce::SObject).to receive(:sfdc_client).and_return client
33
34
  end
34
35
 
35
36
  describe "has_many_query" do
@@ -49,39 +50,41 @@ describe ActiveForce::SObject do
49
50
  end
50
51
 
51
52
  describe 'to_s' do
52
- it "should retrun a OSQL statment" do
53
- post.comments.to_s.should ==
54
- "SELECT Id FROM Comment__c WHERE Post__c = '1'"
53
+ it "should retrun a SOQL statment" do
54
+ soql = "SELECT Id, PostId FROM Comment__c WHERE PostId = '1'"
55
+ expect(post.comments.to_s).to eq soql
55
56
  end
56
57
  end
57
58
 
58
59
  end
59
60
 
60
61
  describe 'has_many(options)' do
62
+ before do
63
+ Post.has_many :comments
64
+ end
61
65
 
62
66
  it 'should allow to send a different query table name' do
63
67
  Post.has_many :ugly_comments, { model: Comment }
64
- post.comments.to_s.should ==
65
- "SELECT Id FROM Comment__c WHERE Post__c = '1'"
68
+ soql = "SELECT Id, PostId FROM Comment__c WHERE PostId = '1'"
69
+ expect(post.ugly_comments.to_s).to eq soql
66
70
  end
67
71
 
68
72
  it 'should allow to change the foreign key' do
69
- Post.has_many :comments, { foreign_key: 'Post' }
70
- post.comments.to_s.should ==
71
- "SELECT Id FROM Comment__c WHERE Post = '1'"
73
+ Post.has_many :comments, { foreign_key: :post }
74
+ Comment.field :post, from: 'PostId'
75
+ soql = "SELECT Id, PostId FROM Comment__c WHERE PostId = '1'"
76
+ expect(post.comments.to_s).to eq soql
72
77
  end
73
78
 
74
79
  it 'should allow to add a where condition' do
75
80
  Post.has_many :comments, { where: '1 = 1' }
76
- post.comments.to_s.should ==
77
- "SELECT Id FROM Comment__c WHERE 1 = 1 AND Post__c = '1'"
81
+ soql = "SELECT Id, PostId FROM Comment__c WHERE 1 = 1 AND PostId = '1'"
82
+ expect(post.comments.to_s).to eq soql
78
83
  end
79
84
 
80
85
  it 'should use a convention name for the foreign key' do
81
- Comment.field :post_id, from: 'PostId'
82
- Post.has_many :comments
83
- post.comments.to_s.should ==
84
- "SELECT Id FROM Comment__c WHERE PostId = '1'"
86
+ soql = "SELECT Id, PostId FROM Comment__c WHERE PostId = '1'"
87
+ expect(post.comments.to_s).to eq soql
85
88
  end
86
89
 
87
90
  end
@@ -89,18 +92,21 @@ describe ActiveForce::SObject do
89
92
  describe "belongs_to" do
90
93
 
91
94
  before do
92
- Comment.belongs_to :post
93
- client.stub(:query).and_return Restforce::Mash.new(id: 1)
95
+ allow(client).to receive(:query).and_return [Restforce::Mash.new(id: 1)]
94
96
  end
95
97
 
96
98
  it "should get the resource it belongs to" do
99
+ Comment.belongs_to :post
97
100
  expect(comment.post).to be_instance_of(Post)
98
101
  end
99
102
 
100
103
  it "should allow to pass a foreign key as options" do
101
- Comment.belongs_to :post, foreign_key: :fancy_post_id
102
- comment.stub(:fancy_post_id).and_return "1"
103
- client.should_receive(:query).with("SELECT Id FROM Post__c WHERE Id = '1' LIMIT 1")
104
+ class Comment < ActiveForce::SObject
105
+ field :fancy_post_id, from: 'PostId'
106
+ belongs_to :post, foreign_key: :fancy_post_id
107
+ end
108
+ allow(comment).to receive(:fancy_post_id).and_return "2"
109
+ expect(client).to receive(:query).with("SELECT Id FROM Post__c WHERE Id = '2' LIMIT 1")
104
110
  comment.post
105
111
  end
106
112
 
@@ -13,76 +13,74 @@ describe ActiveForce::Query do
13
13
 
14
14
  describe ".all" do
15
15
  it "table should return table name" do
16
- @query.all.table.should be(@query.table)
16
+ expect(@query.all.table).to eq(@query.table)
17
17
  end
18
18
 
19
19
  it "fields should return fields" do
20
- @query.all.fields.should == @query.fields
20
+ expect(@query.all.fields).to eq @query.fields
21
21
  end
22
22
  end
23
23
 
24
24
  describe ".all.to_s" do
25
25
  it "should return a query for all records" do
26
- @query.all.to_s.should == "SELECT Id, name, etc FROM table_name"
26
+ expect(@query.all.to_s).to eq "SELECT Id, name, etc FROM table_name"
27
27
  end
28
28
 
29
29
  it "should ignore dupicated attributes in select statment" do
30
30
  @query.fields ['Id', 'name', 'etc']
31
- @query.all.to_s.should == "SELECT Id, name, etc FROM table_name"
31
+ expect(@query.all.to_s).to eq "SELECT Id, name, etc FROM table_name"
32
32
  end
33
33
  end
34
34
 
35
35
  describe ".where" do
36
36
  it "should add a where condition to a query" do
37
- @query.where("name like '%a%'").to_s.should == "SELECT Id, name, etc FROM table_name WHERE name like '%a%'"
37
+ expect(@query.where("name like '%a%'").to_s).to eq "SELECT Id, name, etc FROM table_name WHERE name like '%a%'"
38
38
  end
39
39
 
40
40
  it "should add multiples conditions to a query" do
41
- @query.where("condition1 = 1").where("condition2 = 2").to_s.should ==
42
- "SELECT Id, name, etc FROM table_name WHERE condition1 = 1 AND condition2 = 2"
41
+ expect(@query.where("condition1 = 1").where("condition2 = 2").to_s).to eq "SELECT Id, name, etc FROM table_name WHERE condition1 = 1 AND condition2 = 2"
43
42
  end
44
43
  end
45
44
 
46
45
  describe ".limit" do
47
46
  it "should add a limit to a query" do
48
- @query.limit("25").to_s.should == "SELECT Id, name, etc FROM table_name LIMIT 25"
47
+ expect(@query.limit("25").to_s).to eq "SELECT Id, name, etc FROM table_name LIMIT 25"
49
48
  end
50
49
  end
51
50
 
52
51
  describe ".limit_value" do
53
52
  it "should return the limit value" do
54
53
  @query.limit(4)
55
- @query.limit_value.should == 4
54
+ expect(@query.limit_value).to eq 4
56
55
  end
57
56
  end
58
57
 
59
58
  describe ".offset" do
60
59
  it "should add an offset to a query" do
61
- @query.offset(4).to_s.should == "SELECT Id, name, etc FROM table_name OFFSET 4"
60
+ expect(@query.offset(4).to_s).to eq "SELECT Id, name, etc FROM table_name OFFSET 4"
62
61
  end
63
62
  end
64
63
 
65
64
  describe ".offset_value" do
66
65
  it "should return the offset value" do
67
66
  @query.offset(4)
68
- @query.offset_value.should == 4
67
+ expect(@query.offset_value).to eq 4
69
68
  end
70
69
  end
71
70
 
72
71
  describe ".find.to_s" do
73
72
  it "should return a query for 1 record" do
74
- @query.find(2).to_s.should == "SELECT Id, name, etc FROM table_name WHERE Id = '2' LIMIT 1"
73
+ expect(@query.find(2).to_s).to eq "SELECT Id, name, etc FROM table_name WHERE Id = '2' LIMIT 1"
75
74
  end
76
75
  end
77
76
 
78
77
  describe ".order" do
79
78
  it "should add a order condition in the statment" do
80
- @query.order("name desc").to_s.should == "SELECT Id, name, etc FROM table_name ORDER BY name desc"
79
+ expect(@query.order("name desc").to_s).to eq "SELECT Id, name, etc FROM table_name ORDER BY name desc"
81
80
  end
82
81
 
83
82
  it "should add a order condition in the statment with WHERE and LIMIT" do
84
- @query.where("condition1 = 1").order("name desc").limit(1).to_s.should ==
85
- "SELECT Id, name, etc FROM table_name WHERE condition1 = 1 ORDER BY name desc LIMIT 1"
83
+ 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"
86
84
  end
87
85
  end
88
86
 
@@ -94,56 +92,47 @@ describe ActiveForce::Query do
94
92
  end
95
93
 
96
94
  it 'should add another select statment on the current select' do
97
- @query.join(@join).to_s.should ==
98
- 'SELECT Id, name, etc, (SELECT Id, name, etc FROM join_table_name) FROM table_name'
95
+ expect(@query.join(@join).to_s).to eq 'SELECT Id, name, etc, (SELECT Id, name, etc FROM join_table_name) FROM table_name'
99
96
  end
100
97
  end
101
98
 
102
99
  describe '.first' do
103
100
  it 'should return the query for the first record' do
104
- @query.first.to_s.should ==
105
- 'SELECT Id, name, etc FROM table_name LIMIT 1'
101
+ expect(@query.first.to_s).to eq 'SELECT Id, name, etc FROM table_name LIMIT 1'
106
102
  end
107
103
  end
108
104
 
109
105
  describe '.last' do
110
106
  it 'should return the query for the last record' do
111
- @query.last.to_s.should ==
112
- 'SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT 1'
107
+ expect(@query.last.to_s).to eq 'SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT 1'
113
108
  end
114
109
  end
115
110
 
116
111
  describe ".count" do
117
112
  it "should return the query for getting the row count" do
118
- @query.count.to_s.should ==
119
- 'SELECT count(Id) FROM table_name'
113
+ expect(@query.count.to_s).to eq 'SELECT count(Id) FROM table_name'
120
114
  end
121
115
 
122
116
  it "should work with a condition" do
123
- @query.where("name = 'cool'").count.to_s.should ==
124
- "SELECT count(Id) FROM table_name WHERE name = 'cool'"
117
+ expect(@query.where("name = 'cool'").count.to_s).to eq "SELECT count(Id) FROM table_name WHERE name = 'cool'"
125
118
  end
126
119
  end
127
120
 
128
121
  describe '.options' do
129
122
  it 'should add a where if the option has a where condition' do
130
- @query.options(where: 'var = 1').to_s.should ==
131
- "SELECT Id, name, etc FROM table_name WHERE var = 1"
123
+ expect(@query.options(where: 'var = 1').to_s).to eq "SELECT Id, name, etc FROM table_name WHERE var = 1"
132
124
  end
133
125
 
134
126
  it 'should add a limit if the option has a limit condition' do
135
- @query.options(limit: 1).to_s.should ==
136
- "SELECT Id, name, etc FROM table_name LIMIT 1"
127
+ expect(@query.options(limit: 1).to_s).to eq "SELECT Id, name, etc FROM table_name LIMIT 1"
137
128
  end
138
129
 
139
130
  it 'should add a order if the option has a order condition' do
140
- @query.options(order: 'name desc').to_s.should ==
141
- "SELECT Id, name, etc FROM table_name ORDER BY name desc"
131
+ expect(@query.options(order: 'name desc').to_s).to eq "SELECT Id, name, etc FROM table_name ORDER BY name desc"
142
132
  end
143
133
 
144
134
  it 'should work with multiples options' do
145
- @query.options(where: 'var = 1', order: 'name desc', limit: 1).to_s.should ==
146
- "SELECT Id, name, etc FROM table_name WHERE var = 1 ORDER BY name desc LIMIT 1"
135
+ 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"
147
136
  end
148
137
  end
149
138
  end
@@ -25,5 +25,13 @@ describe ActiveForce::SObject do
25
25
  expect(EnforcedTableName.table_name).to eq('Forced__c')
26
26
  end
27
27
 
28
+ it "doesn't include a namespace" do
29
+ module Foo
30
+ class Bar < ActiveForce::SObject
31
+ end
32
+ end
33
+
34
+ expect(Foo::Bar.table_name).to eq('Bar__c')
35
+ end
28
36
  end
29
37
  end
@@ -2,20 +2,18 @@ require 'spec_helper'
2
2
 
3
3
  describe ActiveForce::SObject do
4
4
  let(:sobject_hash) { YAML.load(fixture('sobject/single_sobject_hash')) }
5
+ let(:client) { double 'Client' }
5
6
 
6
7
  before do
7
- ::Client = double('Client')
8
- end
9
-
10
- after do
11
- Object.send :remove_const, 'Client'
8
+ allow(ActiveForce::SObject).to receive(:sfdc_client).and_return client
12
9
  end
13
10
 
14
11
  describe ".new" do
15
- it "create with valid values" do
16
- @SObject = Whizbang.new
17
- expect(@SObject).to be_an_instance_of Whizbang
12
+
13
+ it 'should assigns values when are passed by parameters' do
14
+ expect(Whizbang.new({ text: 'some text' }).text).to eq 'some text'
18
15
  end
16
+
19
17
  end
20
18
 
21
19
  describe ".build" do
@@ -41,6 +39,38 @@ describe ActiveForce::SObject do
41
39
  expect(Whizbang.attribute_names).to include(name)
42
40
  end
43
41
  end
42
+
43
+ it "uses Salesforce API naming conventions by default" do
44
+ expect(Whizbang.mappings[:estimated_close_date]).to eq 'Estimated_Close_Date__c'
45
+ end
46
+
47
+ describe 'having an id' do
48
+ it 'has one by default' do
49
+ expect(Territory.new).to respond_to(:id)
50
+ expect(Territory.mappings[:id]).to eq 'Id'
51
+ end
52
+
53
+ it 'can be overridden' do
54
+ expect(Quota.new).to respond_to(:id)
55
+ expect(Quota.mappings[:id]).to eq 'Bar_Id__c'
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#update' do
61
+
62
+ subject do
63
+ Whizbang.new
64
+ end
65
+
66
+ before do
67
+ expect(client).to receive(:update!).and_return('id')
68
+ end
69
+
70
+ it 'delegates to the Client with create!' do
71
+ expect(subject.update({ text: 'some text' })).to be_a Whizbang
72
+ end
73
+
44
74
  end
45
75
 
46
76
  describe '#create' do
@@ -50,7 +80,7 @@ describe ActiveForce::SObject do
50
80
  end
51
81
 
52
82
  before do
53
- Client.should_receive(:create!).and_return('id')
83
+ expect(client).to receive(:create!).and_return('id')
54
84
  end
55
85
 
56
86
  it 'delegates to the Client with create!' do
@@ -64,36 +94,36 @@ describe ActiveForce::SObject do
64
94
 
65
95
  end
66
96
 
97
+ describe 'self.create' do
98
+
99
+ before do
100
+ expect(client).to receive(:create!).and_return('id')
101
+ end
102
+
103
+ it 'should create a new instance' do
104
+ expect(Whizbang.create({ text: 'some text' })).to be_a Whizbang
105
+ end
106
+
107
+ end
108
+
67
109
  describe "#count" do
68
110
  let(:count_response){ [Restforce::Mash.new(expr0: 1)] }
69
111
 
70
112
  it "responds to count" do
71
- Whizbang.should respond_to(:count)
113
+ expect(Whizbang).to respond_to(:count)
72
114
  end
73
115
 
74
116
  it "sends the query to the client" do
75
- expect(Client).to receive(:query).and_return(count_response)
117
+ expect(client).to receive(:query).and_return(count_response)
76
118
  expect(Whizbang.count).to eq(1)
77
119
  end
78
120
 
79
121
  end
80
122
 
81
- # describe "first" do
82
- # it "should return the first value" do
83
- # binding.pry
84
- # expect(Whizbang.first).to eq(1)
85
- # end
86
- # end
87
-
88
123
  describe "#find_by" do
89
- it "responds to find_by" do
90
- Whizbang.should respond_to(:find_by)
91
- end
92
-
93
124
  it "should query the client, with the SFDC field names and correctly enclosed values" do
94
- Client.should_receive(:query).with("SELECT Id FROM Whizbang__c WHERE Id = 123 AND Text_Label = 'foo' LIMIT 1")
125
+ expect(client).to receive(:query).with("SELECT #{Whizbang.fields.join ', '} FROM Whizbang__c WHERE Id = 123 AND Text_Label = 'foo' LIMIT 1")
95
126
  Whizbang.find_by id: 123, text: "foo"
96
127
  end
97
128
  end
98
-
99
129
  end
@@ -2,6 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe ActiveForce do
4
4
  it 'should have a version number' do
5
- ActiveForce::VERSION.should_not be_nil
5
+ expect(ActiveForce::VERSION).to_not be_nil
6
6
  end
7
7
  end
data/spec/spec_helper.rb CHANGED
@@ -6,3 +6,8 @@ require 'restforce'
6
6
  require 'active_force'
7
7
  Dir["./spec/support/**/*"].sort.each { |f| require f }
8
8
  require 'pry'
9
+
10
+ class Territory < ActiveForce::SObject; end
11
+ class Quota < ActiveForce::SObject
12
+ field :id, from: 'Bar_Id__c'
13
+ end
@@ -6,5 +6,6 @@ class Whizbang < ActiveForce::SObject
6
6
  field :date, from: 'Date_Label'
7
7
  field :datetime, from: 'DateTime_Label'
8
8
  field :picklist_multiselect, from: 'Picklist_Multiselect_Label'
9
+ field :estimated_close_date
9
10
 
10
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_force
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eloy Espinaco
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2014-08-15 00:00:00.000000000 Z
14
+ date: 2014-08-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: active_attr
@@ -170,3 +170,4 @@ test_files:
170
170
  - spec/spec_helper.rb
171
171
  - spec/support/fixture_helpers.rb
172
172
  - spec/support/whizbang.rb
173
+ has_rdoc: