hubspot-api-ruby 0.8.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 +7 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +18 -0
- data/README.md +295 -0
- data/RELEASING.md +6 -0
- data/Rakefile +32 -0
- data/hubspot-api-ruby.gemspec +42 -0
- data/lib/hubspot-api-ruby.rb +39 -0
- data/lib/hubspot/blog.rb +98 -0
- data/lib/hubspot/collection.rb +41 -0
- data/lib/hubspot/company.rb +160 -0
- data/lib/hubspot/company_properties.rb +59 -0
- data/lib/hubspot/config.rb +63 -0
- data/lib/hubspot/connection.rb +152 -0
- data/lib/hubspot/contact.rb +110 -0
- data/lib/hubspot/contact_list.rb +129 -0
- data/lib/hubspot/contact_properties.rb +59 -0
- data/lib/hubspot/deal.rb +173 -0
- data/lib/hubspot/deal_pipeline.rb +58 -0
- data/lib/hubspot/deal_properties.rb +59 -0
- data/lib/hubspot/deprecator.rb +7 -0
- data/lib/hubspot/engagement.rb +222 -0
- data/lib/hubspot/event.rb +21 -0
- data/lib/hubspot/exceptions.rb +18 -0
- data/lib/hubspot/file.rb +38 -0
- data/lib/hubspot/form.rb +95 -0
- data/lib/hubspot/oauth.rb +50 -0
- data/lib/hubspot/owner.rb +57 -0
- data/lib/hubspot/paged_collection.rb +35 -0
- data/lib/hubspot/properties.rb +123 -0
- data/lib/hubspot/railtie.rb +10 -0
- data/lib/hubspot/resource.rb +270 -0
- data/lib/hubspot/subscription.rb +37 -0
- data/lib/hubspot/topic.rb +37 -0
- data/lib/hubspot/utils.rb +127 -0
- data/lib/tasks/hubspot.rake +53 -0
- data/spec/factories/companies.rb +9 -0
- data/spec/factories/contacts.rb +10 -0
- data/spec/lib/hubspot-ruby_spec.rb +12 -0
- data/spec/lib/hubspot/blog_spec.rb +150 -0
- data/spec/lib/hubspot/company_properties_spec.rb +410 -0
- data/spec/lib/hubspot/company_spec.rb +340 -0
- data/spec/lib/hubspot/config_spec.rb +87 -0
- data/spec/lib/hubspot/connection_spec.rb +214 -0
- data/spec/lib/hubspot/contact_list_spec.rb +301 -0
- data/spec/lib/hubspot/contact_properties_spec.rb +245 -0
- data/spec/lib/hubspot/contact_spec.rb +223 -0
- data/spec/lib/hubspot/deal_pipeline_spec.rb +85 -0
- data/spec/lib/hubspot/deal_properties_spec.rb +262 -0
- data/spec/lib/hubspot/deal_spec.rb +185 -0
- data/spec/lib/hubspot/deprecator_spec.rb +15 -0
- data/spec/lib/hubspot/engagement_spec.rb +177 -0
- data/spec/lib/hubspot/event_spec.rb +33 -0
- data/spec/lib/hubspot/file_spec.rb +38 -0
- data/spec/lib/hubspot/form_spec.rb +189 -0
- data/spec/lib/hubspot/owner_spec.rb +56 -0
- data/spec/lib/hubspot/properties_spec.rb +45 -0
- data/spec/lib/hubspot/resource_spec.rb +54 -0
- data/spec/lib/hubspot/topic_spec.rb +23 -0
- data/spec/lib/hubspot/utils_spec.rb +164 -0
- data/spec/lib/tasks/hubspot_spec.rb +119 -0
- data/spec/shared_examples/saveable_resource.rb +45 -0
- data/spec/shared_examples/updateable_resource.rb +87 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/support/capture_output.rb +21 -0
- data/spec/support/cassette_helper.rb +19 -0
- data/spec/support/hubspot_api_helpers.rb +13 -0
- data/spec/support/rake.rb +46 -0
- data/spec/support/tests_helper.rb +17 -0
- data/spec/support/vcr.rb +16 -0
- metadata +369 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module Hubspot
|
4
|
+
class OAuth < Connection
|
5
|
+
include HTTParty
|
6
|
+
|
7
|
+
DEFAULT_OAUTH_HEADERS = {"Content-Type" => "application/x-www-form-urlencoded;charset=utf-8"}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def refresh(token, params={}, options={})
|
11
|
+
oauth_post(token_url, { grant_type: "refresh_token", refresh_token: token }.merge(params),
|
12
|
+
options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(code, params={}, options={})
|
16
|
+
oauth_post(token_url, { grant_type: "authorization_code", code: code }.merge(params),
|
17
|
+
options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def authorize_url(scopes, params={})
|
21
|
+
client_id = params[:client_id] || Hubspot::Config.client_id
|
22
|
+
redirect_uri = params[:redirect_uri] || Hubspot::Config.redirect_uri
|
23
|
+
scopes = Array.wrap(scopes)
|
24
|
+
|
25
|
+
"https://app.hubspot.com/oauth/authorize?client_id=#{client_id}&scope=#{scopes.join("%20")}&redirect_uri=#{redirect_uri}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def token_url
|
29
|
+
token_url = Hubspot::Config.base_url + "/oauth/v1/token"
|
30
|
+
end
|
31
|
+
|
32
|
+
def oauth_post(url, params, options={})
|
33
|
+
no_parse = options[:no_parse] || false
|
34
|
+
|
35
|
+
body = {
|
36
|
+
client_id: Hubspot::Config.client_id,
|
37
|
+
client_secret: Hubspot::Config.client_secret,
|
38
|
+
redirect_uri: Hubspot::Config.redirect_uri,
|
39
|
+
}.merge(params)
|
40
|
+
|
41
|
+
response = post(url, body: body, headers: DEFAULT_OAUTH_HEADERS)
|
42
|
+
log_request_and_response url, response, body
|
43
|
+
|
44
|
+
raise(Hubspot::RequestError.new(response)) unless response.success?
|
45
|
+
|
46
|
+
no_parse ? response : response.parsed_response
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Hubspot
|
2
|
+
#
|
3
|
+
# HubSpot Owners API
|
4
|
+
#
|
5
|
+
# {http://developers.hubspot.com/docs/methods/owners/get_owners}
|
6
|
+
#
|
7
|
+
# TODO: Create an Owner
|
8
|
+
# TODO: Update an Owner
|
9
|
+
# TODO: Delete an Owner
|
10
|
+
class Owner
|
11
|
+
GET_OWNER_PATH = '/owners/v2/owners/:owner_id' # GET
|
12
|
+
GET_OWNERS_PATH = '/owners/v2/owners' # GET
|
13
|
+
CREATE_OWNER_PATH = '/owners/v2/owners' # POST
|
14
|
+
UPDATE_OWNER_PATH = '/owners/v2/owners/:owner_id' # PUT
|
15
|
+
DELETE_OWNER_PATH = '/owners/v2/owners/:owner_id' # DELETE
|
16
|
+
|
17
|
+
|
18
|
+
attr_reader :properties, :owner_id, :email
|
19
|
+
|
20
|
+
def initialize(property_hash)
|
21
|
+
@properties = property_hash
|
22
|
+
@owner_id = @properties['ownerId']
|
23
|
+
@email = @properties['email']
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](property)
|
27
|
+
@properties[property]
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def all(include_inactive=false)
|
32
|
+
path = GET_OWNERS_PATH
|
33
|
+
params = { includeInactive: include_inactive }
|
34
|
+
response = Hubspot::Connection.get_json(path, params)
|
35
|
+
response.map { |r| new(r) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def find(id, include_inactive=false)
|
39
|
+
path = GET_OWNER_PATH
|
40
|
+
response = Hubspot::Connection.get_json(path, owner_id: id,
|
41
|
+
include_inactive: include_inactive)
|
42
|
+
new(response)
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_by_email(email, include_inactive=false)
|
46
|
+
path = GET_OWNERS_PATH
|
47
|
+
params = { email: email, includeInactive: include_inactive }
|
48
|
+
response = Hubspot::Connection.get_json(path, params)
|
49
|
+
response.blank? ? nil : new(response.first)
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_by_emails(emails, include_inactive=false)
|
53
|
+
emails.map { |email| find_by_email(email, include_inactive) }.reject(&:blank?)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Hubspot::PagedCollection < Hubspot::Collection
|
2
|
+
attr_accessor :offset, :limit
|
3
|
+
|
4
|
+
def initialize(opts = {}, &block)
|
5
|
+
@limit_param = opts.delete(:limit_param) || "limit"
|
6
|
+
@limit = opts.delete(:limit) || 25
|
7
|
+
@offset_param = opts.delete(:offset_param) || "offset"
|
8
|
+
@offset = opts.delete(:offset)
|
9
|
+
|
10
|
+
super(opts, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def more?
|
14
|
+
@has_more
|
15
|
+
end
|
16
|
+
|
17
|
+
def next_offset
|
18
|
+
@next_offset
|
19
|
+
end
|
20
|
+
|
21
|
+
def next_page?
|
22
|
+
@has_more
|
23
|
+
end
|
24
|
+
|
25
|
+
def next_page
|
26
|
+
@offset = next_offset
|
27
|
+
fetch
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
def fetch
|
33
|
+
@resources, @next_offset, @has_more = @fetch_proc.call(@options, @offset, @limit)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Hubspot
|
2
|
+
class Properties
|
3
|
+
|
4
|
+
PROPERTY_SPECS = {
|
5
|
+
group_field_names: %w(name displayName displayOrder properties),
|
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
|
+
valid_types: %w(string number bool date datetime enumeration),
|
9
|
+
options: %w(description value label hidden displayOrder)
|
10
|
+
}
|
11
|
+
DEFAULT_PROPERTY = 'email'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# TODO: properties can be set as configuration
|
15
|
+
# TODO: find the way how to set a list of Properties + merge same property key if present from opts
|
16
|
+
def add_default_parameters(opts={})
|
17
|
+
if opts.keys.map(&:to_s).include? 'property'
|
18
|
+
opts
|
19
|
+
else
|
20
|
+
opts.merge(property: DEFAULT_PROPERTY)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def all(path, opts={}, filter={})
|
25
|
+
response = Hubspot::Connection.get_json(path, opts)
|
26
|
+
filter_results(response, :groupName, filter[:include], filter[:exclude])
|
27
|
+
end
|
28
|
+
|
29
|
+
def groups(path, opts={}, filter={})
|
30
|
+
response = Hubspot::Connection.get_json(path, opts)
|
31
|
+
filter_results(response, :name, filter[:include], filter[:exclude])
|
32
|
+
end
|
33
|
+
|
34
|
+
def create!(path, params={})
|
35
|
+
post_data = valid_property_params(params)
|
36
|
+
return nil if post_data.blank?
|
37
|
+
Hubspot::Connection.post_json(path, params: {}, body: post_data)
|
38
|
+
end
|
39
|
+
|
40
|
+
def update!(path, property_name, params={})
|
41
|
+
post_data = valid_property_params(params)
|
42
|
+
return nil if post_data.blank?
|
43
|
+
Hubspot::Connection.put_json(path, params: { property_name: property_name }, body: post_data)
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete!(path, property_name)
|
47
|
+
response = Hubspot::Connection.delete_json(path, property_name: property_name)
|
48
|
+
response.parsed_response
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_group!(path, params={})
|
52
|
+
post_data = valid_group_params(params)
|
53
|
+
return nil if post_data.blank?
|
54
|
+
Hubspot::Connection.post_json(path, params: {}, body: post_data)
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_group!(path, group_name, params={})
|
58
|
+
post_data = valid_group_params(params)
|
59
|
+
return nil if post_data.blank?
|
60
|
+
Hubspot::Connection.put_json(path, params: { group_name: group_name }, body: post_data)
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete_group!(path, group_name)
|
64
|
+
response = Hubspot::Connection.delete_json(path, group_name: group_name)
|
65
|
+
response.parsed_response
|
66
|
+
end
|
67
|
+
|
68
|
+
def same?(src, dst)
|
69
|
+
src_params = valid_params(src)
|
70
|
+
dst_params = valid_params(dst)
|
71
|
+
src_params.eql?(dst_params)
|
72
|
+
# hash_same?(src_params, dst_params)
|
73
|
+
end
|
74
|
+
|
75
|
+
def valid_params(params={})
|
76
|
+
valid_property_params(params)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def filter_results(results, key, include, exclude)
|
82
|
+
key = key.to_s
|
83
|
+
results.select { |result|
|
84
|
+
(include.blank? || include.include?(result[key])) &&
|
85
|
+
(exclude.blank? || !exclude.include?(result[key]))
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid_property_params(params)
|
90
|
+
return {} if params.blank?
|
91
|
+
result = params.slice(*PROPERTY_SPECS[:field_names])
|
92
|
+
result.delete('fieldType') unless check_field_type(result['fieldType'])
|
93
|
+
result.delete('type') unless check_type(result['type'])
|
94
|
+
result['options'] = valid_option_params(result['options'])
|
95
|
+
result
|
96
|
+
end
|
97
|
+
|
98
|
+
def valid_group_params(params)
|
99
|
+
return {} if params.blank?
|
100
|
+
result = params.slice(*PROPERTY_SPECS[:group_field_names])
|
101
|
+
result['properties'] = valid_property_params(result['properties']) unless result['properties'].blank?
|
102
|
+
result
|
103
|
+
end
|
104
|
+
|
105
|
+
def check_field_type(val)
|
106
|
+
return true if PROPERTY_SPECS[:valid_field_types].include?(val)
|
107
|
+
puts "Invalid field type: #{val}"
|
108
|
+
false
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_type(val)
|
112
|
+
return true if PROPERTY_SPECS[:valid_types].include?(val)
|
113
|
+
puts "Invalid type: #{val}"
|
114
|
+
false
|
115
|
+
end
|
116
|
+
|
117
|
+
def valid_option_params(options)
|
118
|
+
return [] if options.blank?
|
119
|
+
options.map { |o| o.slice(*PROPERTY_SPECS[:options]) }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
module Hubspot
|
2
|
+
class Resource
|
3
|
+
|
4
|
+
class_attribute :id_field, instance_writer: false
|
5
|
+
class_attribute :property_name_field, instance_writer: false
|
6
|
+
class_attribute :update_method, instance_writer: false
|
7
|
+
|
8
|
+
self.id_field = "id"
|
9
|
+
self.property_name_field = "property"
|
10
|
+
self.update_method = "put"
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def from_result(result)
|
14
|
+
resource = new(result[id_field])
|
15
|
+
resource.send(:initialize_from, result.with_indifferent_access)
|
16
|
+
resource
|
17
|
+
end
|
18
|
+
|
19
|
+
def find(id)
|
20
|
+
instance = new(id)
|
21
|
+
instance.reload
|
22
|
+
end
|
23
|
+
|
24
|
+
def create(properties = {})
|
25
|
+
request = {
|
26
|
+
properties: Hubspot::Utils.hash_to_properties(properties.stringify_keys, key_name: property_name_field)
|
27
|
+
}
|
28
|
+
response = Hubspot::Connection.post_json(create_path, params: {}, body: request)
|
29
|
+
from_result(response)
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(id, properties = {})
|
33
|
+
begin
|
34
|
+
update!(id, properties)
|
35
|
+
rescue Hubspot::RequestError => e
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def update!(id, properties = {})
|
41
|
+
request = {
|
42
|
+
properties: Hubspot::Utils.hash_to_properties(properties.stringify_keys, key_name: property_name_field)
|
43
|
+
}
|
44
|
+
|
45
|
+
if update_method == "put"
|
46
|
+
response = Hubspot::Connection.put_json(update_path, params: { id: id, no_parse: true }, body: request)
|
47
|
+
else
|
48
|
+
response = Hubspot::Connection.post_json(update_path, params: { id: id, no_parse: true }, body: request)
|
49
|
+
end
|
50
|
+
|
51
|
+
response.success?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(id_or_properties = nil)
|
56
|
+
@changes = HashWithIndifferentAccess.new
|
57
|
+
@properties = HashWithIndifferentAccess.new
|
58
|
+
|
59
|
+
if id_or_properties.is_a?(Integer) || id_or_properties.nil?
|
60
|
+
@id = id_or_properties
|
61
|
+
elsif id_or_properties.is_a?(Hash)
|
62
|
+
@id = id_or_properties.delete(id_field) || id_or_properties.delete(:id)
|
63
|
+
|
64
|
+
add_accessors(id_or_properties.keys)
|
65
|
+
id_or_properties.each do |k, v|
|
66
|
+
send "#{k}=", v
|
67
|
+
end
|
68
|
+
else
|
69
|
+
raise InvalidParams.new("#{self.class.name} must be initialized with an ID, hash, or nil")
|
70
|
+
end
|
71
|
+
|
72
|
+
@persisted = @id.present?
|
73
|
+
@deleted = false
|
74
|
+
end
|
75
|
+
|
76
|
+
def id
|
77
|
+
@id
|
78
|
+
end
|
79
|
+
|
80
|
+
def id=(id)
|
81
|
+
@id = id
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_i
|
85
|
+
@id
|
86
|
+
end
|
87
|
+
|
88
|
+
def metadata
|
89
|
+
@metadata
|
90
|
+
end
|
91
|
+
|
92
|
+
def changes
|
93
|
+
@changes
|
94
|
+
end
|
95
|
+
|
96
|
+
def changed?
|
97
|
+
!@changes.empty?
|
98
|
+
end
|
99
|
+
|
100
|
+
def [](name)
|
101
|
+
@changes[name] || @properties[name]
|
102
|
+
end
|
103
|
+
|
104
|
+
def reload
|
105
|
+
raise(Hubspot::InvalidParams.new("Resource must have an ID")) if @id.nil?
|
106
|
+
|
107
|
+
response = Hubspot::Connection.get_json(find_path, id: @id)
|
108
|
+
initialize_from(response.with_indifferent_access)
|
109
|
+
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def persisted?
|
114
|
+
@persisted
|
115
|
+
end
|
116
|
+
|
117
|
+
def save
|
118
|
+
request = {
|
119
|
+
properties: Hubspot::Utils.hash_to_properties(@changes.stringify_keys, key_name: property_name_field)
|
120
|
+
}
|
121
|
+
|
122
|
+
if persisted?
|
123
|
+
if update_method == "put"
|
124
|
+
response = Hubspot::Connection.put_json(update_path, params: { id: @id }, body: request)
|
125
|
+
else
|
126
|
+
response = Hubspot::Connection.post_json(update_path, params: { id: @id }, body: request)
|
127
|
+
end
|
128
|
+
|
129
|
+
update_from_changes
|
130
|
+
else
|
131
|
+
response = Hubspot::Connection.post_json(create_path, params: {}, body: request)
|
132
|
+
|
133
|
+
# Grab the new ID from the response
|
134
|
+
@id = response[id_field]
|
135
|
+
|
136
|
+
# Update the fields with the response
|
137
|
+
initialize_from(response.with_indifferent_access)
|
138
|
+
end
|
139
|
+
|
140
|
+
@persisted = true
|
141
|
+
true
|
142
|
+
end
|
143
|
+
|
144
|
+
def update(properties)
|
145
|
+
if properties && !properties.is_a?(Hash)
|
146
|
+
raise ArgumentError, "When assigning properties, you must pass a hash as an argument."
|
147
|
+
end
|
148
|
+
|
149
|
+
@changes = @changes.merge(properties)
|
150
|
+
save
|
151
|
+
end
|
152
|
+
|
153
|
+
def delete
|
154
|
+
raise(Hubspot::InvalidParams.new("Resource must have an ID")) if @id.nil?
|
155
|
+
|
156
|
+
Hubspot::Connection.delete_json(delete_path, id: @id)
|
157
|
+
|
158
|
+
@deleted = true
|
159
|
+
@changes = HashWithIndifferentAccess.new
|
160
|
+
true
|
161
|
+
end
|
162
|
+
|
163
|
+
def deleted?
|
164
|
+
@deleted
|
165
|
+
end
|
166
|
+
|
167
|
+
protected
|
168
|
+
|
169
|
+
def self.create_path
|
170
|
+
begin
|
171
|
+
self::CREATE_PATH
|
172
|
+
rescue NameError
|
173
|
+
raise "CREATE_PATH not defined for #{self.class.name}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def create_path
|
178
|
+
self.class.create_path
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.find_path
|
182
|
+
begin
|
183
|
+
self::FIND_PATH
|
184
|
+
rescue NameError
|
185
|
+
raise "FIND_PATH not defined for #{self.class.name}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def find_path
|
190
|
+
self.class.find_path
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.update_path
|
194
|
+
begin
|
195
|
+
self::UPDATE_PATH
|
196
|
+
rescue NameError
|
197
|
+
raise "UPDATE_PATH not defined for #{self.class.name}"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def update_path
|
202
|
+
self.class.update_path
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.delete_path
|
206
|
+
begin
|
207
|
+
self::DELETE_PATH
|
208
|
+
rescue NameError
|
209
|
+
raise "CREATE_PATH not defined for #{self.class.name}"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def delete_path
|
214
|
+
self.class.delete_path
|
215
|
+
end
|
216
|
+
|
217
|
+
def initialize_from(response)
|
218
|
+
@properties = response["properties"] || HashWithIndifferentAccess.new
|
219
|
+
@metadata = response.except "properties"
|
220
|
+
|
221
|
+
add_accessors(@properties.keys)
|
222
|
+
|
223
|
+
# Clear any changes
|
224
|
+
@changes = HashWithIndifferentAccess.new
|
225
|
+
end
|
226
|
+
|
227
|
+
def update_from_changes
|
228
|
+
@changes.each do |k, v|
|
229
|
+
@properties[k] ||= {}
|
230
|
+
@properties[k]["value"] = v
|
231
|
+
end
|
232
|
+
|
233
|
+
# Clear any changes
|
234
|
+
@changes = HashWithIndifferentAccess.new
|
235
|
+
end
|
236
|
+
|
237
|
+
def add_accessors(keys)
|
238
|
+
singleton_class.instance_eval do
|
239
|
+
keys.each do |k|
|
240
|
+
# Define a getter
|
241
|
+
define_method(k) { @changes[k.to_sym] || @properties.dig(k, "value") }
|
242
|
+
|
243
|
+
# Define a setter
|
244
|
+
define_method("#{k}=") do |v|
|
245
|
+
@changes[k.to_sym] = v
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def method_missing(method_name, *arguments, &block)
|
252
|
+
# When assigning a missing attribute define the accessors and set the value
|
253
|
+
if method_name.to_s.end_with?("=")
|
254
|
+
attr = method_name.to_s[0...-1].to_sym
|
255
|
+
add_accessors([attr])
|
256
|
+
|
257
|
+
# Call the new setter
|
258
|
+
return send(method_name, arguments[0])
|
259
|
+
elsif @properties.key?(method_name)
|
260
|
+
return @properties[method_name]
|
261
|
+
else
|
262
|
+
super
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def respond_to_missing?(method_name, include_private = false)
|
267
|
+
(@properties && @properties.key?(method_name)) || super
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|