hubspot-ruby 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +11 -0
- data/hubspot-ruby.gemspec +4 -3
- data/lib/hubspot/connection.rb +3 -3
- data/lib/hubspot/contact.rb +44 -17
- data/lib/hubspot/contact_list.rb +3 -1
- data/lib/hubspot/deal.rb +24 -0
- data/lib/hubspot/deal_pipeline.rb +15 -0
- data/lib/hubspot/engagement.rb +44 -1
- data/lib/hubspot/properties.rb +8 -4
- data/lib/hubspot/subscription.rb +29 -0
- data/lib/hubspot-ruby.rb +1 -0
- data/lib/tasks/properties.rake +49 -0
- data/spec/lib/hubspot/company_properties_spec.rb +10 -2
- data/spec/lib/hubspot/company_spec.rb +3 -3
- data/spec/lib/hubspot/connection_spec.rb +11 -5
- data/spec/lib/hubspot/contact_list_spec.rb +14 -2
- data/spec/lib/hubspot/contact_properties_spec.rb +10 -2
- data/spec/lib/hubspot/contact_spec.rb +57 -0
- data/spec/lib/hubspot/deal_properties_spec.rb +10 -2
- data/spec/lib/hubspot/engagement_spec.rb +89 -45
- data/spec/live/deal_pipeline_spec.rb +35 -0
- metadata +10 -8
- data/Gemfile.lock +0 -99
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ab6604b85e4945f699f88491ad18191a9ceda03
|
4
|
+
data.tar.gz: 8b01545c2f8c6d5e51d610233abcd55b2d9027d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5b5d2e3c28f811c44b14e232e308cc2af19cf4050f6963dfa00ce7c0b59022c73fde892adf9e7fe0a5a73cb6d067b512cefdae13510b06164dd0630e4430b63
|
7
|
+
data.tar.gz: 3381873ef4e85c46cbf4a17608c66c29414fa40fdf911fb7463a58be117f7a307b06905d3db269f5a56f8d98b68f70f4ed632a6816a7c8d6ffb6801af7770281
|
data/README.md
CHANGED
@@ -37,6 +37,12 @@ Here's what you can do for now:
|
|
37
37
|
Hubspot::Contact.create!("email@address.com", {firstname: "First", lastname: "Last"})
|
38
38
|
```
|
39
39
|
|
40
|
+
#### In batches
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
Hubspot::Contact.create_or_update!([{email: 'smith@example.com', firstname: 'First', lastname: 'Last'}])
|
44
|
+
```
|
45
|
+
|
40
46
|
### Find a contact
|
41
47
|
|
42
48
|
These methods will return a `Hubspot::Contact` object if successful, `nil` otherwise:
|
@@ -54,6 +60,11 @@ Given an instance of `Hubspot::Contact`, update its attributes with:
|
|
54
60
|
contact.update!({firstname: "First", lastname: "Last"})
|
55
61
|
```
|
56
62
|
|
63
|
+
#### In batches
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
Hubspot::Contact.create_or_update!([{vid: '12345', firstname: 'First', lastname: 'Last'}])
|
67
|
+
```
|
57
68
|
## Contributing to hubspot-ruby
|
58
69
|
|
59
70
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
data/hubspot-ruby.gemspec
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "hubspot-ruby"
|
3
|
-
s.version = "0.
|
3
|
+
s.version = "0.3.0"
|
4
4
|
s.require_paths = ["lib"]
|
5
5
|
s.authors = ["Andrew DiMichele"]
|
6
6
|
s.description = "hubspot-ruby is a wrapper for the HubSpot REST API"
|
7
|
-
s.files = [".rspec", "Gemfile", "
|
7
|
+
s.files = [".rspec", "Gemfile", "Guardfile", "LICENSE.txt", "README.md", "RELEASING.md", "Rakefile", "hubspot-ruby.gemspec"]
|
8
8
|
s.files += Dir["lib/**/*.rb"]
|
9
|
+
s.files += Dir["lib/**/*.rake"]
|
9
10
|
s.files += Dir["spec/**/*.rb"]
|
10
11
|
s.homepage = "http://github.com/adimichele/hubspot-ruby"
|
11
12
|
s.summary = "hubspot-ruby is a wrapper for the HubSpot REST API"
|
@@ -16,7 +17,7 @@ Gem::Specification.new do |s|
|
|
16
17
|
|
17
18
|
# Add development-only dependencies here
|
18
19
|
s.add_development_dependency("rake")
|
19
|
-
s.add_development_dependency("rspec")
|
20
|
+
s.add_development_dependency("rspec", "~> 2.0")
|
20
21
|
s.add_development_dependency("rr")
|
21
22
|
s.add_development_dependency("webmock", "< 1.10")
|
22
23
|
s.add_development_dependency("vcr")
|
data/lib/hubspot/connection.rb
CHANGED
@@ -58,7 +58,7 @@ module Hubspot
|
|
58
58
|
|
59
59
|
params.each do |k,v|
|
60
60
|
if path.match(":#{k}")
|
61
|
-
path.gsub!(":#{k}",v.to_s)
|
61
|
+
path.gsub!(":#{k}", CGI.escape(v.to_s))
|
62
62
|
params.delete(k)
|
63
63
|
end
|
64
64
|
end
|
@@ -68,13 +68,13 @@ module Hubspot
|
|
68
68
|
v.is_a?(Array) ? v.map { |value| param_string(k,value) } : param_string(k,v)
|
69
69
|
end.join("&")
|
70
70
|
|
71
|
-
path += "?" if query.present?
|
71
|
+
path += path.include?('?') ? '&' : "?" if query.present?
|
72
72
|
base_url + path + query
|
73
73
|
end
|
74
74
|
|
75
75
|
# convert into milliseconds since epoch
|
76
76
|
def converted_value(value)
|
77
|
-
value.is_a?(Time) ? (value.to_i * 1000) : value
|
77
|
+
value.is_a?(Time) ? (value.to_i * 1000) : CGI.escape(value.to_s)
|
78
78
|
end
|
79
79
|
|
80
80
|
def param_string(key,value)
|
data/lib/hubspot/contact.rb
CHANGED
@@ -6,24 +6,26 @@ module Hubspot
|
|
6
6
|
#
|
7
7
|
# TODO: work on all endpoints that can specify contact properties, property mode etc... as params. cf pending specs
|
8
8
|
class Contact
|
9
|
-
CREATE_CONTACT_PATH
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
9
|
+
CREATE_CONTACT_PATH = '/contacts/v1/contact'
|
10
|
+
BATCH_CREATE_OR_UPDATE_PATH = '/contacts/v1/contact/batch/'
|
11
|
+
GET_CONTACT_BY_EMAIL_PATH = '/contacts/v1/contact/email/:contact_email/profile'
|
12
|
+
GET_CONTACTS_BY_EMAIL_PATH = '/contacts/v1/contact/emails/batch'
|
13
|
+
GET_CONTACT_BY_ID_PATH = '/contacts/v1/contact/vid/:contact_id/profile'
|
14
|
+
CONTACT_BATCH_PATH = '/contacts/v1/contact/vids/batch'
|
15
|
+
GET_CONTACT_BY_UTK_PATH = '/contacts/v1/contact/utk/:contact_utk/profile'
|
16
|
+
GET_CONTACTS_BY_UTK_PATH = '/contacts/v1/contact/utks/batch'
|
17
|
+
UPDATE_CONTACT_PATH = '/contacts/v1/contact/vid/:contact_id/profile'
|
18
|
+
DESTROY_CONTACT_PATH = '/contacts/v1/contact/vid/:contact_id'
|
19
|
+
CONTACTS_PATH = '/contacts/v1/lists/all/contacts/all'
|
20
|
+
RECENTLY_UPDATED_PATH = '/contacts/v1/lists/recently_updated/contacts/recent'
|
21
|
+
CREATE_OR_UPDATE_PATH = '/contacts/v1/contact/createOrUpdate/email/:contact_email'
|
22
|
+
QUERY_PATH = '/contacts/v1/search/query'
|
22
23
|
|
23
24
|
class << self
|
24
25
|
# {https://developers.hubspot.com/docs/methods/contacts/create_contact}
|
25
26
|
def create!(email, params={})
|
26
|
-
params_with_email = params.stringify_keys
|
27
|
+
params_with_email = params.stringify_keys
|
28
|
+
params_with_email = params.stringify_keys.merge('email' => email) if email
|
27
29
|
post_data = {properties: Hubspot::Utils.hash_to_properties(params_with_email)}
|
28
30
|
response = Hubspot::Connection.post_json(CREATE_CONTACT_PATH, params: {}, body: post_data )
|
29
31
|
new(response)
|
@@ -33,21 +35,22 @@ module Hubspot
|
|
33
35
|
# {https://developers.hubspot.com/docs/methods/contacts/get_recently_updated_contacts}
|
34
36
|
def all(opts={})
|
35
37
|
recent = opts.delete(:recent) { false }
|
38
|
+
paged = opts.delete(:paged) { false }
|
36
39
|
path, opts =
|
37
40
|
if recent
|
38
|
-
[
|
41
|
+
[RECENTLY_UPDATED_PATH, Hubspot::ContactProperties.add_default_parameters(opts)]
|
39
42
|
else
|
40
43
|
[CONTACTS_PATH, opts]
|
41
44
|
end
|
42
45
|
|
43
46
|
response = Hubspot::Connection.get_json(path, opts)
|
44
|
-
response['contacts'].map { |c| new(c) }
|
47
|
+
response['contacts'].map! { |c| new(c) }
|
48
|
+
paged ? response : response['contacts']
|
45
49
|
end
|
46
50
|
|
47
51
|
# TODO: create or update a contact
|
48
52
|
# PATH /contacts/v1/contact/createOrUpdate/email/:contact_email
|
49
53
|
# API endpoint: https://developers.hubspot.com/docs/methods/contacts/create_or_update
|
50
|
-
# + batch mode: https://developers.hubspot.com/docs/methods/contacts/batch_create_or_update
|
51
54
|
def createOrUpdate(email, params={})
|
52
55
|
post_data = {properties: Hubspot::Utils.hash_to_properties(params.stringify_keys)}
|
53
56
|
response = Hubspot::Connection.post_json(CREATE_OR_UPDATE_PATH, params: { contact_email: email }, body: post_data )
|
@@ -56,6 +59,28 @@ module Hubspot
|
|
56
59
|
contact
|
57
60
|
end
|
58
61
|
|
62
|
+
# NOTE: Performance is best when calls are limited to 100 or fewer contacts
|
63
|
+
# {https://developers.hubspot.com/docs/methods/contacts/batch_create_or_update}
|
64
|
+
def create_or_update!(contacts)
|
65
|
+
query = contacts.map do |ch|
|
66
|
+
contact_hash = ch.with_indifferent_access
|
67
|
+
contact_param = {
|
68
|
+
properties: Hubspot::Utils.hash_to_properties(contact_hash.except(:vid))
|
69
|
+
}
|
70
|
+
if contact_hash[:vid]
|
71
|
+
contact_param.merge!(vid: contact_hash[:vid])
|
72
|
+
elsif contact_hash[:email]
|
73
|
+
contact_param.merge!(email: contact_hash[:email])
|
74
|
+
else
|
75
|
+
raise Hubspot::InvalidParams, 'expecting vid or email for contact'
|
76
|
+
end
|
77
|
+
contact_param
|
78
|
+
end
|
79
|
+
Hubspot::Connection.post_json(BATCH_CREATE_OR_UPDATE_PATH,
|
80
|
+
params: {},
|
81
|
+
body: query)
|
82
|
+
end
|
83
|
+
|
59
84
|
# NOTE: problem with batch api endpoint
|
60
85
|
# {https://developers.hubspot.com/docs/methods/contacts/get_contact}
|
61
86
|
# {https://developers.hubspot.com/docs/methods/contacts/get_batch_by_vid}
|
@@ -119,10 +144,12 @@ module Hubspot
|
|
119
144
|
end
|
120
145
|
|
121
146
|
attr_reader :properties, :vid, :is_new
|
147
|
+
attr_reader :is_contact
|
122
148
|
|
123
149
|
def initialize(response_hash)
|
124
150
|
props = response_hash['properties']
|
125
151
|
@properties = Hubspot::Utils.properties_to_hash(props) unless props.blank?
|
152
|
+
@is_contact = response_hash["is-contact"]
|
126
153
|
@vid = response_hash['vid']
|
127
154
|
end
|
128
155
|
|
data/lib/hubspot/contact_list.rb
CHANGED
@@ -78,13 +78,15 @@ module Hubspot
|
|
78
78
|
# NOTE: caching functionality can be dependant of the nature of the list, if dynamic or not ...
|
79
79
|
bypass_cache = opts.delete(:bypass_cache) { false }
|
80
80
|
recent = opts.delete(:recent) { false }
|
81
|
+
paged = opts.delete(:paged) { false }
|
81
82
|
|
82
83
|
if bypass_cache || @contacts.nil?
|
83
84
|
path = recent ? RECENT_CONTACTS_PATH : CONTACTS_PATH
|
84
85
|
opts[:list_id] = @id
|
85
86
|
|
86
87
|
response = Hubspot::Connection.get_json(path, Hubspot::ContactProperties.add_default_parameters(opts))
|
87
|
-
@contacts = response['contacts'].map { |c| Hubspot::Contact.new(c) }
|
88
|
+
@contacts = response['contacts'].map! { |c| Hubspot::Contact.new(c) }
|
89
|
+
paged ? response : @contacts
|
88
90
|
else
|
89
91
|
@contacts
|
90
92
|
end
|
data/lib/hubspot/deal.rb
CHANGED
@@ -11,6 +11,8 @@ module Hubspot
|
|
11
11
|
DEAL_PATH = "/deals/v1/deal/:deal_id"
|
12
12
|
RECENT_UPDATED_PATH = "/deals/v1/deal/recent/modified"
|
13
13
|
UPDATE_DEAL_PATH = '/deals/v1/deal/:deal_id'
|
14
|
+
ASSOCIATE_DEAL_PATH = '/deals/v1/deal/:deal_id/associations/:OBJECTTYPE?id=:objectId'
|
15
|
+
ASSOCIATED_DEAL_PATH = "/deals/v1/deal/associated/:objectType/:objectId"
|
14
16
|
|
15
17
|
attr_reader :properties
|
16
18
|
attr_reader :portal_id
|
@@ -36,6 +38,17 @@ module Hubspot
|
|
36
38
|
new(response)
|
37
39
|
end
|
38
40
|
|
41
|
+
# Associate a deal with a contact or company
|
42
|
+
# {http://developers.hubspot.com/docs/methods/deals/associate_deal}
|
43
|
+
# Usage
|
44
|
+
# Hubspot::Deal.associate!(45146940, [], [52])
|
45
|
+
def associate!(deal_id, company_ids=[], vids=[])
|
46
|
+
objecttype = company_ids.any? ? 'COMPANY' : 'CONTACT'
|
47
|
+
object_ids = (company_ids.any? ? company_ids : vids).join('&id=')
|
48
|
+
Hubspot::Connection.put_json(ASSOCIATE_DEAL_PATH, params: { deal_id: deal_id, OBJECTTYPE: objecttype, objectId: object_ids}, body: {})
|
49
|
+
end
|
50
|
+
|
51
|
+
|
39
52
|
def find(deal_id)
|
40
53
|
response = Hubspot::Connection.get_json(DEAL_PATH, { deal_id: deal_id })
|
41
54
|
new(response)
|
@@ -49,6 +62,17 @@ module Hubspot
|
|
49
62
|
response = Hubspot::Connection.get_json(RECENT_UPDATED_PATH, opts)
|
50
63
|
response['results'].map { |d| new(d) }
|
51
64
|
end
|
65
|
+
|
66
|
+
# Find all deals associated to a company
|
67
|
+
# {http://developers.hubspot.com/docs/methods/deals/get-associated-deals}
|
68
|
+
# @param company [Hubspot::Company] the company
|
69
|
+
# @return [Array] Array of Hubspot::Deal records
|
70
|
+
def find_by_company(company)
|
71
|
+
path = ASSOCIATED_DEAL_PATH
|
72
|
+
params = { objectType: :company, objectId: company.vid }
|
73
|
+
response = Hubspot::Connection.get_json(path, params)
|
74
|
+
response["results"].map { |deal_id| find(deal_id) }
|
75
|
+
end
|
52
76
|
|
53
77
|
end
|
54
78
|
|
@@ -34,6 +34,21 @@ module Hubspot
|
|
34
34
|
response = Hubspot::Connection.get_json(PIPELINES_PATH, {})
|
35
35
|
response.map { |p| new(p) }
|
36
36
|
end
|
37
|
+
|
38
|
+
# Creates a DealPipeline
|
39
|
+
# {https://developers.hubspot.com/docs/methods/deal-pipelines/create-deal-pipeline}
|
40
|
+
# @return [Hubspot::PipeLine] Company record
|
41
|
+
def create!(post_data={})
|
42
|
+
response = Hubspot::Connection.post_json(PIPELINES_PATH, params: {}, body: post_data)
|
43
|
+
new(response)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Destroys deal_pipeline
|
48
|
+
# {http://developers.hubspot.com/docs/methods/companies/delete_company}
|
49
|
+
# @return [TrueClass] true
|
50
|
+
def destroy!
|
51
|
+
Hubspot::Connection.delete_json(PIPELINE_PATH, pipeline_id: @pipeline_id)
|
37
52
|
end
|
38
53
|
|
39
54
|
def [](stage)
|
data/lib/hubspot/engagement.rb
CHANGED
@@ -33,7 +33,7 @@ module Hubspot
|
|
33
33
|
def find(engagement_id)
|
34
34
|
begin
|
35
35
|
response = Hubspot::Connection.get_json(ENGAGEMENT_PATH, { engagement_id: engagement_id })
|
36
|
-
new(HashWithIndifferentAccess.new(response))
|
36
|
+
response ? new(HashWithIndifferentAccess.new(response)) : nil
|
37
37
|
rescue Hubspot::RequestError => ex
|
38
38
|
if ex.response.code == 404
|
39
39
|
return nil
|
@@ -130,4 +130,47 @@ module Hubspot
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
end
|
133
|
+
|
134
|
+
class EngagementCall < Engagement
|
135
|
+
def body
|
136
|
+
metadata['body']
|
137
|
+
end
|
138
|
+
|
139
|
+
def contact_ids
|
140
|
+
associations['contactIds']
|
141
|
+
end
|
142
|
+
|
143
|
+
def company_ids
|
144
|
+
associations['companyIds']
|
145
|
+
end
|
146
|
+
|
147
|
+
def deal_ids
|
148
|
+
associations['dealIds']
|
149
|
+
end
|
150
|
+
|
151
|
+
class << self
|
152
|
+
def create!(contact_vid, body, duration, owner_id = nil, deal_id = nil, status = 'COMPLETED', time = nil)
|
153
|
+
data = {
|
154
|
+
engagement: {
|
155
|
+
type: 'CALL'
|
156
|
+
},
|
157
|
+
associations: {
|
158
|
+
contactIds: [contact_vid],
|
159
|
+
dealIds: [deal_id],
|
160
|
+
ownerIds: [owner_id]
|
161
|
+
},
|
162
|
+
metadata: {
|
163
|
+
body: body,
|
164
|
+
status: status,
|
165
|
+
durationMilliseconds: duration
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
data[:engagement][:timestamp] = (time.to_i) * 1000 if time
|
170
|
+
data[:engagement][:owner_id] = owner_id if owner_id
|
171
|
+
|
172
|
+
super(data)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
133
176
|
end
|
data/lib/hubspot/properties.rb
CHANGED
@@ -3,18 +3,22 @@ module Hubspot
|
|
3
3
|
|
4
4
|
PROPERTY_SPECS = {
|
5
5
|
group_field_names: %w(name displayName displayOrder properties),
|
6
|
-
field_names: %w(name groupName description fieldType formField type displayOrder label options),
|
7
|
-
valid_field_types: %w(textarea select text date file number radio checkbox),
|
6
|
+
field_names: %w(name groupName description fieldType formField type displayOrder label options showCurrencySymbol),
|
7
|
+
valid_field_types: %w(textarea select text date file number radio checkbox booleancheckbox),
|
8
8
|
valid_types: %w(string number bool datetime enumeration),
|
9
9
|
options: %w(description value label hidden displayOrder)
|
10
10
|
}
|
11
|
+
DEFAULT_PROPERTY = 'email'
|
11
12
|
|
12
13
|
class << self
|
13
14
|
# TODO: properties can be set as configuration
|
14
15
|
# TODO: find the way how to set a list of Properties + merge same property key if present from opts
|
15
16
|
def add_default_parameters(opts={})
|
16
|
-
|
17
|
-
|
17
|
+
if opts.keys.map(&:to_s).include? 'property'
|
18
|
+
opts
|
19
|
+
else
|
20
|
+
opts.merge(property: DEFAULT_PROPERTY)
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
def all(path, opts={}, filter={})
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Hubspot
|
2
|
+
class Subscription
|
3
|
+
SUBSCRIPTIONS_PATH = '/email/public/v1/subscriptions'
|
4
|
+
TIMELINE_PATH = '/email/public/v1/subscriptions/timeline'
|
5
|
+
SUBSCRIPTION_PATH = '/email/public/v1/subscriptions/:email_address'
|
6
|
+
|
7
|
+
attr_reader :subscribed
|
8
|
+
attr_reader :marked_as_spam
|
9
|
+
attr_reader :bounced
|
10
|
+
attr_reader :status
|
11
|
+
attr_reader :subscription_statuses
|
12
|
+
|
13
|
+
def initialize(response_hash)
|
14
|
+
@subscribed = response_hash['subscribed']
|
15
|
+
@marked_as_spam = response_hash['markedAsSpam']
|
16
|
+
@bounced = response_hash['bounced']
|
17
|
+
@status = response_hash['status']
|
18
|
+
@subscription_statuses = response_hash['SubscriptionStatuses']
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def status(email)
|
23
|
+
response = Hubspot::Connection.get_json(SUBSCRIPTION_PATH, {email_address: email})
|
24
|
+
new(response)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
data/lib/hubspot-ruby.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'hubspot-ruby'
|
2
|
+
|
3
|
+
namespace :hubspot do
|
4
|
+
desc 'Dump properties to file'
|
5
|
+
task :dump_properties, [:kind, :file, :hapikey, :include, :exclude] do |_, args|
|
6
|
+
hapikey = args[:hapikey] || ENV['HUBSPOT_API_KEY']
|
7
|
+
kind = args[:kind]
|
8
|
+
unless %w(contact deal).include?(kind)
|
9
|
+
raise ArgumentError, ':kind must be either "contact" or "deal"'
|
10
|
+
end
|
11
|
+
klass = kind == 'contact' ? Hubspot::ContactProperties : Hubspot::DealProperties
|
12
|
+
props = Hubspot::Utils::dump_properties(klass, hapikey, build_filter(args))
|
13
|
+
if args[:file].blank?
|
14
|
+
puts JSON.pretty_generate(props)
|
15
|
+
else
|
16
|
+
File.open(args[:file], 'w') do |f|
|
17
|
+
f.write(JSON.pretty_generate(props))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Restore properties from file'
|
23
|
+
task :restore_properties, [:kind, :file, :hapikey, :dry_run] do |_, args|
|
24
|
+
hapikey = args[:hapikey] || ENV['HUBSPOT_API_KEY']
|
25
|
+
if args[:file].blank?
|
26
|
+
raise ArgumentError, ':file is a required parameter'
|
27
|
+
end
|
28
|
+
kind = args[:kind]
|
29
|
+
unless %w(contact deal).include?(kind)
|
30
|
+
raise ArgumentError, ':kind must be either "contact" or "deal"'
|
31
|
+
end
|
32
|
+
klass = kind == 'contact' ? Hubspot::ContactProperties : Hubspot::DealProperties
|
33
|
+
file = File.read(args[:file])
|
34
|
+
props = JSON.parse(file)
|
35
|
+
Hubspot::Utils.restore_properties(klass, hapikey, props, args[:dry_run] != 'false')
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def build_filter(args)
|
41
|
+
{ include: val_to_array(args[:include]),
|
42
|
+
exclude: val_to_array(args[:exclude])
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def val_to_array(val)
|
47
|
+
val.blank? ? val : val.split(/\W+/)
|
48
|
+
end
|
49
|
+
end
|
@@ -1,8 +1,16 @@
|
|
1
1
|
describe Hubspot::CompanyProperties do
|
2
2
|
describe '.add_default_parameters' do
|
3
|
-
|
3
|
+
let(:opts) { {} }
|
4
|
+
subject { Hubspot::CompanyProperties.add_default_parameters(opts) }
|
4
5
|
context 'default parameters' do
|
5
|
-
|
6
|
+
context 'without property parameter' do
|
7
|
+
its([:property]) { should == 'email' }
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'with property parameter' do
|
11
|
+
let(:opts) { {property: 'name' } }
|
12
|
+
its([:property]) { should == 'name'}
|
13
|
+
end
|
6
14
|
end
|
7
15
|
end
|
8
16
|
|
@@ -108,15 +108,15 @@ describe Hubspot::Contact do
|
|
108
108
|
cassette 'find_all_recent_companies'
|
109
109
|
|
110
110
|
it 'must get the companies list' do
|
111
|
-
companies = Hubspot::Company.all(
|
111
|
+
companies = Hubspot::Company.all(recently_updated: true)
|
112
112
|
expect(companies.size).to eql 20
|
113
113
|
|
114
114
|
first, last = companies.first, companies.last
|
115
115
|
expect(first).to be_a Hubspot::Company
|
116
|
-
expect(first.vid).to eql
|
116
|
+
expect(first.vid).to eql 318615742
|
117
117
|
|
118
118
|
expect(last).to be_a Hubspot::Company
|
119
|
-
expect(last.vid).to eql
|
119
|
+
expect(last.vid).to eql 359899290
|
120
120
|
end
|
121
121
|
end
|
122
122
|
end
|
@@ -76,30 +76,36 @@ describe Hubspot::Connection do
|
|
76
76
|
|
77
77
|
context "with an interpolated param" do
|
78
78
|
let(:params){ {email: "email@address.com"} }
|
79
|
-
it{ should == "https://api.hubapi.com/test/email
|
79
|
+
it{ should == "https://api.hubapi.com/test/email%40address.com/profile?hapikey=demo" }
|
80
80
|
end
|
81
81
|
|
82
82
|
context "with multiple interpolated params" do
|
83
83
|
let(:path){ "/test/:email/:id/profile" }
|
84
84
|
let(:params){{email: "email@address.com", id: 1234}}
|
85
|
-
it{ should == "https://api.hubapi.com/test/email
|
85
|
+
it{ should == "https://api.hubapi.com/test/email%40address.com/1234/profile?hapikey=demo" }
|
86
86
|
end
|
87
87
|
|
88
88
|
context "with query params" do
|
89
89
|
let(:params){{email: "email@address.com", id: 1234}}
|
90
|
-
it{ should == "https://api.hubapi.com/test/email
|
90
|
+
it{ should == "https://api.hubapi.com/test/email%40address.com/profile?id=1234&hapikey=demo" }
|
91
91
|
|
92
92
|
context "containing a time" do
|
93
93
|
let(:start_time) { Time.now }
|
94
94
|
let(:params){{email: "email@address.com", id: 1234, start: start_time}}
|
95
|
-
it{ should == "https://api.hubapi.com/test/email
|
95
|
+
it{ should == "https://api.hubapi.com/test/email%40address.com/profile?id=1234&start=#{start_time.to_i * 1000}&hapikey=demo" }
|
96
96
|
end
|
97
97
|
|
98
98
|
context "containing a range" do
|
99
99
|
let(:start_time) { Time.now }
|
100
100
|
let(:end_time) { Time.now + 1.year }
|
101
101
|
let(:params){{email: "email@address.com", id: 1234, created__range: start_time..end_time }}
|
102
|
-
it{ should == "https://api.hubapi.com/test/email
|
102
|
+
it{ should == "https://api.hubapi.com/test/email%40address.com/profile?id=1234&created__range=#{start_time.to_i * 1000}&created__range=#{end_time.to_i * 1000}&hapikey=demo" }
|
103
|
+
end
|
104
|
+
|
105
|
+
context "containing an array of strings" do
|
106
|
+
let(:path){ "/test/emails" }
|
107
|
+
let(:params){{batch_email: %w(email1@example.com email2@example.com)}}
|
108
|
+
it{ should == "https://api.hubapi.com/test/emails?email=email1%40example.com&email=email2%40example.com&hapikey=demo" }
|
103
109
|
end
|
104
110
|
end
|
105
111
|
|
@@ -32,12 +32,24 @@ describe Hubspot::ContactList do
|
|
32
32
|
|
33
33
|
let(:list) { Hubspot::ContactList.new(example_contact_list_hash) }
|
34
34
|
|
35
|
-
it 'returns by
|
35
|
+
it 'returns by default 20 contact lists' do
|
36
36
|
expect(list.contacts.count).to eql 20
|
37
37
|
contact = list.contacts.first
|
38
38
|
expect(contact).to be_a(Hubspot::Contact)
|
39
39
|
end
|
40
40
|
|
41
|
+
it 'returns by default 20 contact lists with paging data' do
|
42
|
+
contact_data = list.contacts({paged: true})
|
43
|
+
contacts = contact_data['contacts']
|
44
|
+
|
45
|
+
expect(contact_data).to have_key 'vid-offset'
|
46
|
+
expect(contact_data).to have_key 'has-more'
|
47
|
+
|
48
|
+
expect(contacts.count).to eql 20
|
49
|
+
contact = contacts.first
|
50
|
+
expect(contact).to be_a(Hubspot::Contact)
|
51
|
+
end
|
52
|
+
|
41
53
|
it 'add default properties to the contacts returned' do
|
42
54
|
contact = list.contacts.first
|
43
55
|
expect(contact.email).to_not be_empty
|
@@ -86,7 +98,7 @@ describe Hubspot::ContactList do
|
|
86
98
|
context 'all list types' do
|
87
99
|
cassette 'find_all_lists'
|
88
100
|
|
89
|
-
it 'returns by
|
101
|
+
it 'returns by default 20 contact lists' do
|
90
102
|
lists = Hubspot::ContactList.all
|
91
103
|
expect(lists.count).to eql 20
|
92
104
|
|
@@ -1,8 +1,16 @@
|
|
1
1
|
describe Hubspot::ContactProperties do
|
2
2
|
describe '.add_default_parameters' do
|
3
|
-
|
3
|
+
let(:opts) { {} }
|
4
|
+
subject { Hubspot::ContactProperties.add_default_parameters(opts) }
|
4
5
|
context 'default parameters' do
|
5
|
-
|
6
|
+
context 'without property parameter' do
|
7
|
+
its([:property]) { should == 'email' }
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'with property parameter' do
|
11
|
+
let(:opts) { {property: 'firstname' } }
|
12
|
+
its([:property]) { should == 'firstname'}
|
13
|
+
end
|
6
14
|
end
|
7
15
|
end
|
8
16
|
|
@@ -84,6 +84,38 @@ describe Hubspot::Contact do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
describe '.create_or_update!' do
|
88
|
+
cassette 'create_or_update'
|
89
|
+
let(:existing_contact) do
|
90
|
+
Hubspot::Contact.create!("morpheus@example.com", firstname: 'Morpheus')
|
91
|
+
end
|
92
|
+
before do
|
93
|
+
Hubspot::Contact.create_or_update!(params)
|
94
|
+
end
|
95
|
+
|
96
|
+
let(:params) do
|
97
|
+
[
|
98
|
+
{
|
99
|
+
vid: existing_contact.vid,
|
100
|
+
email: existing_contact.email,
|
101
|
+
firstname: 'Neo'
|
102
|
+
},
|
103
|
+
{
|
104
|
+
email: 'smith@example.com',
|
105
|
+
firstname: 'Smith'
|
106
|
+
}
|
107
|
+
]
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'creates and updates contacts' do
|
111
|
+
contact = Hubspot::Contact.find_by_id existing_contact.vid
|
112
|
+
expect(contact.properties['firstname']).to eql 'Neo'
|
113
|
+
latest_contact_email = Hubspot::Contact.all(recent: true).first.email
|
114
|
+
new_contact = Hubspot::Contact.find_by_email(latest_contact_email)
|
115
|
+
expect(new_contact.properties['firstname']).to eql 'Smith'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
87
119
|
describe '.find_by_email' do
|
88
120
|
context 'given an uniq email' do
|
89
121
|
cassette 'contact_find_by_email'
|
@@ -229,6 +261,31 @@ describe Hubspot::Contact do
|
|
229
261
|
expect(last['lastname']).to eql 'Morgan'
|
230
262
|
end
|
231
263
|
|
264
|
+
it 'must get the contacts list with paging data' do
|
265
|
+
contact_data = Hubspot::Contact.all({paged: true})
|
266
|
+
contacts = contact_data['contacts']
|
267
|
+
|
268
|
+
expect(contacts.size).to eql 20 # default page size
|
269
|
+
|
270
|
+
first = contacts.first
|
271
|
+
last = contacts.last
|
272
|
+
|
273
|
+
expect(contact_data).to have_key 'vid-offset'
|
274
|
+
expect(contact_data).to have_key 'has-more'
|
275
|
+
|
276
|
+
expect(first).to be_a Hubspot::Contact
|
277
|
+
expect(first.vid).to eql 154835
|
278
|
+
expect(first['firstname']).to eql 'HubSpot'
|
279
|
+
expect(first['lastname']).to eql 'Test'
|
280
|
+
|
281
|
+
expect(last).to be_a Hubspot::Contact
|
282
|
+
expect(last.vid).to eql 196199
|
283
|
+
expect(last['firstname']).to eql 'Eleanor'
|
284
|
+
expect(last['lastname']).to eql 'Morgan'
|
285
|
+
expect(contact_data['has-more']).to eql true
|
286
|
+
expect(contact_data['vid-offset']).to eql 196199
|
287
|
+
end
|
288
|
+
|
232
289
|
it 'must filter only 2 contacts' do
|
233
290
|
contacts = Hubspot::Contact.all(count: 2)
|
234
291
|
expect(contacts.size).to eql 2
|
@@ -1,8 +1,16 @@
|
|
1
1
|
describe Hubspot::DealProperties do
|
2
2
|
describe '.add_default_parameters' do
|
3
|
-
|
3
|
+
let(:opts) { {} }
|
4
|
+
subject { Hubspot::DealProperties.add_default_parameters(opts) }
|
4
5
|
context 'default parameters' do
|
5
|
-
|
6
|
+
context 'without property parameter' do
|
7
|
+
its([:property]) { should == 'email' }
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'with property parameter' do
|
11
|
+
let(:opts) { {property: 'dealname' } }
|
12
|
+
its([:property]) { should == 'dealname'}
|
13
|
+
end
|
6
14
|
end
|
7
15
|
end
|
8
16
|
|
@@ -19,69 +19,113 @@ describe Hubspot::Engagement do
|
|
19
19
|
its (:id) { should == 51484873 }
|
20
20
|
end
|
21
21
|
|
22
|
-
describe
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
describe 'EngagementNote' do
|
23
|
+
describe ".create!" do
|
24
|
+
cassette "engagement_create"
|
25
|
+
body = "Test note"
|
26
|
+
subject { Hubspot::EngagementNote.create!(nil, body) }
|
27
|
+
its(:id) { should_not be_nil }
|
28
|
+
its(:body) { should eql body }
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
describe ".find" do
|
32
|
+
cassette "engagement_find"
|
33
|
+
let(:engagement) {Hubspot::EngagementNote.new(example_engagement_hash)}
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
it 'must find by the engagement id' do
|
36
|
+
find_engagement = Hubspot::EngagementNote.find(engagement.id)
|
37
|
+
find_engagement.id.should eql engagement.id
|
38
|
+
find_engagement.body.should eql engagement.body
|
39
|
+
end
|
38
40
|
end
|
39
|
-
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
describe ".find_by_company" do
|
43
|
+
cassette "engagement_find_by_country"
|
44
|
+
let(:engagement) {Hubspot::EngagementNote.new(example_associated_engagement_hash)}
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
it 'must find by company id' do
|
47
|
+
find_engagements = Hubspot::EngagementNote.find_by_company(engagement.associations["companyIds"].first)
|
48
|
+
find_engagements.should_not be_nil
|
49
|
+
find_engagements.any?{|engagement| engagement.id == engagement.id and engagement.body == engagement.body}.should be_true
|
50
|
+
end
|
49
51
|
end
|
50
|
-
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
describe ".find_by_contact" do
|
54
|
+
cassette "engagement_find_by_contact"
|
55
|
+
let(:engagement) {Hubspot::EngagementNote.new(example_associated_engagement_hash)}
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
it 'must find by contact id' do
|
58
|
+
find_engagements = Hubspot::EngagementNote.find_by_contact(engagement.associations["contactIds"].first)
|
59
|
+
find_engagements.should_not be_nil
|
60
|
+
find_engagements.any?{|engagement| engagement.id == engagement.id and engagement.body == engagement.body}.should be_true
|
61
|
+
end
|
60
62
|
end
|
61
|
-
end
|
62
63
|
|
63
|
-
|
64
|
-
|
64
|
+
describe ".find_by_association" do
|
65
|
+
cassette "engagement_find_by_association"
|
66
|
+
|
67
|
+
it 'must raise for fake association type' do
|
68
|
+
expect {
|
69
|
+
Hubspot::EngagementNote.find_by_association(1, 'FAKE_TYPE')
|
70
|
+
}.to raise_error
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#destroy!' do
|
75
|
+
cassette 'engagement_destroy'
|
76
|
+
|
77
|
+
let(:engagement) {Hubspot::EngagementNote.create!(nil, 'test note') }
|
65
78
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
79
|
+
it 'should remove from hubspot' do
|
80
|
+
expect(Hubspot::Engagement.find(engagement.id)).to_not be_nil
|
81
|
+
|
82
|
+
expect(engagement.destroy!).to be_true
|
83
|
+
expect(engagement.destroyed?).to be_true
|
84
|
+
|
85
|
+
expect(Hubspot::Engagement.find(engagement.id)).to be_nil
|
86
|
+
end
|
70
87
|
end
|
71
88
|
end
|
72
89
|
|
73
|
-
describe '
|
74
|
-
|
90
|
+
describe 'EngagementCall' do
|
91
|
+
let(:example_engagement_hash) do
|
92
|
+
VCR.use_cassette("engagement_call_example") do
|
93
|
+
HTTParty.get("https://api.hubapi.com/engagements/v1/engagements/4709059?hapikey=demo").parsed_response
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe ".create!" do
|
98
|
+
cassette "engagement_call_create"
|
99
|
+
body = "Test call"
|
100
|
+
subject { Hubspot::EngagementCall.create!(nil, body, 0) }
|
101
|
+
its(:id) { should_not be_nil }
|
102
|
+
its(:body) { should eql body }
|
103
|
+
end
|
104
|
+
|
105
|
+
describe ".find" do
|
106
|
+
cassette "engagement_call_find"
|
107
|
+
let(:engagement) { Hubspot::EngagementNote.new(example_engagement_hash) }
|
108
|
+
|
109
|
+
it 'must find by the engagement id' do
|
110
|
+
find_engagement = Hubspot::EngagementNote.find(engagement.id)
|
111
|
+
find_engagement.id.should eql engagement.id
|
112
|
+
find_engagement.body.should eql engagement.body
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#destroy!' do
|
117
|
+
cassette 'engagement_call_destroy'
|
75
118
|
|
76
|
-
|
119
|
+
let(:engagement) { Hubspot::EngagementCall.create!(nil, 'test call', 0) }
|
77
120
|
|
78
|
-
|
79
|
-
|
121
|
+
it 'should remove from hubspot' do
|
122
|
+
expect(Hubspot::Engagement.find(engagement.id)).to_not be_nil
|
80
123
|
|
81
|
-
|
82
|
-
|
124
|
+
expect(engagement.destroy!).to be_true
|
125
|
+
expect(engagement.destroyed?).to be_true
|
83
126
|
|
84
|
-
|
127
|
+
expect(Hubspot::Engagement.find(engagement.id)).to be_nil
|
128
|
+
end
|
85
129
|
end
|
86
130
|
end
|
87
131
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
describe 'Deals pipeline API Live test', live: true do
|
2
|
+
|
3
|
+
before do
|
4
|
+
Hubspot.configure hapikey: 'demo'
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:params) do
|
8
|
+
{
|
9
|
+
'label' => 'auto pipeline1',
|
10
|
+
'stages' => [
|
11
|
+
{
|
12
|
+
'label' => 'initial state',
|
13
|
+
'displayOrder' => 0,
|
14
|
+
'probability' => 0.5
|
15
|
+
},
|
16
|
+
{
|
17
|
+
'label' => 'next state',
|
18
|
+
'displayOrder' => 1,
|
19
|
+
'probability' => 0.9
|
20
|
+
}
|
21
|
+
]
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should create, find, update and destroy' do
|
26
|
+
pipeline = Hubspot::DealPipeline.create!(params)
|
27
|
+
|
28
|
+
expect(pipeline.label).to eql 'auto pipeline1'
|
29
|
+
expect(pipeline.stages.size).to eql 2
|
30
|
+
expect(pipeline.stages.first['label']).to eql 'initial state'
|
31
|
+
expect(pipeline.stages[1]['label']).to eql 'next state'
|
32
|
+
|
33
|
+
pipeline.destroy!
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hubspot-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew DiMichele
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -56,16 +56,16 @@ dependencies:
|
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
61
|
+
version: '2.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
68
|
+
version: '2.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rr
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -200,7 +200,6 @@ extra_rdoc_files: []
|
|
200
200
|
files:
|
201
201
|
- ".rspec"
|
202
202
|
- Gemfile
|
203
|
-
- Gemfile.lock
|
204
203
|
- Guardfile
|
205
204
|
- LICENSE.txt
|
206
205
|
- README.md
|
@@ -225,8 +224,10 @@ files:
|
|
225
224
|
- lib/hubspot/owner.rb
|
226
225
|
- lib/hubspot/properties.rb
|
227
226
|
- lib/hubspot/railtie.rb
|
227
|
+
- lib/hubspot/subscription.rb
|
228
228
|
- lib/hubspot/topic.rb
|
229
229
|
- lib/hubspot/utils.rb
|
230
|
+
- lib/tasks/properties.rake
|
230
231
|
- spec/lib/hubspot-ruby_spec.rb
|
231
232
|
- spec/lib/hubspot/blog_spec.rb
|
232
233
|
- spec/lib/hubspot/company_properties_spec.rb
|
@@ -249,6 +250,7 @@ files:
|
|
249
250
|
- spec/live/companies_properties_integration_spec.rb
|
250
251
|
- spec/live/contacts_integration_spec.rb
|
251
252
|
- spec/live/contacts_properties_integration_spec.rb
|
253
|
+
- spec/live/deal_pipeline_spec.rb
|
252
254
|
- spec/live/deal_properties_integration_spec.rb
|
253
255
|
- spec/live/deals_integration_spec.rb
|
254
256
|
- spec/spec_helper.rb
|
@@ -273,7 +275,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
273
275
|
version: '0'
|
274
276
|
requirements: []
|
275
277
|
rubyforge_project:
|
276
|
-
rubygems_version: 2.5.
|
278
|
+
rubygems_version: 2.4.5.2
|
277
279
|
signing_key:
|
278
280
|
specification_version: 4
|
279
281
|
summary: hubspot-ruby is a wrapper for the HubSpot REST API
|
data/Gemfile.lock
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
hubspot-ruby (0.2.0)
|
5
|
-
activesupport (>= 3.0.0)
|
6
|
-
httparty (>= 0.10.0)
|
7
|
-
|
8
|
-
GEM
|
9
|
-
remote: http://rubygems.org/
|
10
|
-
specs:
|
11
|
-
activesupport (3.2.12)
|
12
|
-
i18n (~> 0.6)
|
13
|
-
multi_json (~> 1.0)
|
14
|
-
addressable (2.3.6)
|
15
|
-
awesome_print (1.1.0)
|
16
|
-
celluloid (0.16.0)
|
17
|
-
timers (~> 4.0.0)
|
18
|
-
coderay (1.1.0)
|
19
|
-
crack (0.4.2)
|
20
|
-
safe_yaml (~> 1.0.0)
|
21
|
-
diff-lcs (1.2.1)
|
22
|
-
ffi (1.9.6)
|
23
|
-
formatador (0.2.5)
|
24
|
-
guard (2.6.1)
|
25
|
-
formatador (>= 0.2.4)
|
26
|
-
listen (~> 2.7)
|
27
|
-
lumberjack (~> 1.0)
|
28
|
-
pry (>= 0.9.12)
|
29
|
-
thor (>= 0.18.1)
|
30
|
-
guard-rspec (3.1.0)
|
31
|
-
guard (>= 1.8)
|
32
|
-
rspec (~> 2.13)
|
33
|
-
hitimes (1.2.2)
|
34
|
-
httparty (0.10.2)
|
35
|
-
multi_json (~> 1.0)
|
36
|
-
multi_xml (>= 0.5.2)
|
37
|
-
i18n (0.6.4)
|
38
|
-
json (1.8.3)
|
39
|
-
listen (2.7.11)
|
40
|
-
celluloid (>= 0.15.2)
|
41
|
-
rb-fsevent (>= 0.9.3)
|
42
|
-
rb-inotify (>= 0.9)
|
43
|
-
lumberjack (1.0.9)
|
44
|
-
method_source (0.8.2)
|
45
|
-
multi_json (1.6.1)
|
46
|
-
multi_xml (0.5.3)
|
47
|
-
pry (0.10.1)
|
48
|
-
coderay (~> 1.1.0)
|
49
|
-
method_source (~> 0.8.1)
|
50
|
-
slop (~> 3.4)
|
51
|
-
rake (10.4.2)
|
52
|
-
rb-fsevent (0.9.4)
|
53
|
-
rb-inotify (0.9.5)
|
54
|
-
ffi (>= 0.5.0)
|
55
|
-
rdoc (4.0.0)
|
56
|
-
json (~> 1.4)
|
57
|
-
rr (1.0.4)
|
58
|
-
rspec (2.13.0)
|
59
|
-
rspec-core (~> 2.13.0)
|
60
|
-
rspec-expectations (~> 2.13.0)
|
61
|
-
rspec-mocks (~> 2.13.0)
|
62
|
-
rspec-core (2.13.1)
|
63
|
-
rspec-expectations (2.13.0)
|
64
|
-
diff-lcs (>= 1.1.3, < 2.0)
|
65
|
-
rspec-mocks (2.13.0)
|
66
|
-
safe_yaml (1.0.4)
|
67
|
-
simplecov (0.7.1)
|
68
|
-
multi_json (~> 1.0)
|
69
|
-
simplecov-html (~> 0.7.1)
|
70
|
-
simplecov-html (0.7.1)
|
71
|
-
slop (3.6.0)
|
72
|
-
thor (0.19.1)
|
73
|
-
timecop (0.7.1)
|
74
|
-
timers (4.0.1)
|
75
|
-
hitimes
|
76
|
-
vcr (2.4.0)
|
77
|
-
webmock (1.9.3)
|
78
|
-
addressable (>= 2.2.7)
|
79
|
-
crack (>= 0.3.2)
|
80
|
-
|
81
|
-
PLATFORMS
|
82
|
-
ruby
|
83
|
-
|
84
|
-
DEPENDENCIES
|
85
|
-
awesome_print
|
86
|
-
bundler
|
87
|
-
guard-rspec
|
88
|
-
hubspot-ruby!
|
89
|
-
rake
|
90
|
-
rdoc
|
91
|
-
rr
|
92
|
-
rspec
|
93
|
-
simplecov
|
94
|
-
timecop
|
95
|
-
vcr
|
96
|
-
webmock (< 1.10)
|
97
|
-
|
98
|
-
BUNDLED WITH
|
99
|
-
1.13.2
|