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 +4 -4
- data/CHANGELOG.md +65 -6
- data/README.md +12 -4
- data/lib/active_force/active_query.rb +7 -8
- data/lib/active_force/association/association.rb +4 -3
- data/lib/active_force/association/belongs_to_association.rb +5 -5
- data/lib/active_force/association/has_many_association.rb +5 -5
- data/lib/active_force/sobject.rb +110 -51
- data/lib/active_force/version.rb +1 -1
- data/lib/generators/active_force/active_force_model/active_force_model_generator.rb +40 -15
- data/lib/generators/active_force/active_force_model/templates/model.rb.erb +2 -13
- data/spec/active_force/active_query_spec.rb +42 -7
- data/spec/active_force/association_spec.rb +29 -23
- data/spec/active_force/query_spec.rb +22 -33
- data/spec/active_force/sobject/table_name_spec.rb +8 -0
- data/spec/active_force/sobject_spec.rb +54 -24
- data/spec/active_force_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -0
- data/spec/support/whizbang.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fc8bc2425de35aa52c303eb28a9165a2601c5f5
|
4
|
+
data.tar.gz: b397002aa1906d23b92d4d20ff08745d349c6cb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef355901a29bd4ff95bf593400f3a52e32e53cd4bec886b5d057984a31092f832b0ce0cf4608462f6379652ad91120d4437100177d4f3912e0051a85abbfa9fb
|
7
|
+
data.tar.gz: 8ce4758557b84eef8662c8f53ff3e301ecdc077d1f824ef2850cb17636d6dde5cab9ed564ca57d3b02ed9a600a188b6e133ebb6eefa8f536803d3a767abcf672
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,70 @@
|
|
1
|
-
#
|
1
|
+
# Changelog
|
2
2
|
|
3
|
-
|
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
|
-
|
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
|
-
|
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
|
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(
|
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
|
16
|
+
query.where association.foreign_key => self.id
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/lib/active_force/sobject.rb
CHANGED
@@ -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, :
|
18
|
+
class_attribute :mappings, :table_name
|
17
19
|
|
18
20
|
class << self
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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,
|
104
|
-
|
105
|
-
|
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
|
116
|
-
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
135
|
-
|
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
|
-
|
139
|
-
|
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
|
data/lib/active_force/version.rb
CHANGED
@@ -1,22 +1,47 @@
|
|
1
|
-
|
1
|
+
module ActiveForce
|
2
|
+
class ActiveForceModelGenerator < Rails::Generators::NamedBase
|
2
3
|
|
3
|
-
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
4
5
|
|
5
|
-
|
6
|
-
argument :attributes, type: :array, default: [],
|
7
|
-
banner: "field[:sales_force_name] field[:sales_force_name]"
|
6
|
+
def create_model_file
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
6
|
-
|
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.
|
8
|
-
sobject.
|
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
|
-
|
18
|
-
|
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 =
|
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 =
|
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.
|
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.
|
15
|
-
comment.
|
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.
|
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
|
53
|
-
|
54
|
-
|
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
|
-
|
65
|
-
|
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:
|
70
|
-
|
71
|
-
|
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
|
-
|
77
|
-
|
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
|
-
|
82
|
-
|
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
|
-
|
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
|
102
|
-
|
103
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
-
::
|
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
|
-
|
16
|
-
|
17
|
-
expect(
|
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
|
-
|
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.
|
113
|
+
expect(Whizbang).to respond_to(:count)
|
72
114
|
end
|
73
115
|
|
74
116
|
it "sends the query to the client" do
|
75
|
-
expect(
|
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
|
-
|
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
|
data/spec/active_force_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/support/whizbang.rb
CHANGED
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
|
+
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-
|
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:
|