hubspot-ruby 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +37 -2
- data/Guardfile +9 -0
- data/Rakefile +1 -1
- data/hubspot-ruby.gemspec +26 -6
- data/lib/hubspot-ruby.rb +3 -1
- data/lib/hubspot/blog.rb +133 -0
- data/lib/hubspot/contact.rb +1 -1
- data/lib/hubspot/topic.rb +53 -0
- data/lib/hubspot/utils.rb +22 -2
- data/lib/hubspot/version.rb +2 -2
- data/spec/fixtures/vcr_cassettes/blog_list.yml +180 -0
- data/spec/fixtures/vcr_cassettes/blog_posts.yml +107 -0
- data/spec/fixtures/vcr_cassettes/blog_posts_list.yml +6463 -0
- data/spec/fixtures/vcr_cassettes/form_post.yml +31 -0
- data/spec/fixtures/vcr_cassettes/one_month_blog_posts_filter_state.yml +11243 -0
- data/spec/fixtures/vcr_cassettes/one_month_blog_posts_list.yml +34963 -0
- data/spec/fixtures/vcr_cassettes/topic_list.yml +48 -0
- data/spec/fixtures/vcr_cassettes/topics_list.yml +86 -0
- data/spec/lib/hubspot/blog_spec.rb +114 -0
- data/spec/lib/hubspot/topic_spec.rb +26 -0
- data/spec/lib/hubspot/utils_spec.rb +13 -0
- metadata +73 -59
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d569b6dba8fd40bb8ab265cdd39642c2051f3d23
|
4
|
+
data.tar.gz: ddc2d7586ac345b9f8bcbf7b0e43e13e1eaba2f8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 96b6ac6cfee35ba8850562928fc598c34c37b0eceed5110376b7bcfc25263c2cdd8a0e4198354512468a17934b3a11c1c07d794718dc8c21ea472e9c75be8d24
|
7
|
+
data.tar.gz: ed7de706aa07a1752d0048cdcb28df121f237e51d5c4897f39e7dddcdbdb37b6143afd73a61483a5812e04a10ea471626db14f119e31eb6669cbc8bb556ad1ef
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -6,9 +6,24 @@ GEM
|
|
6
6
|
multi_json (~> 1.0)
|
7
7
|
addressable (2.3.3)
|
8
8
|
awesome_print (1.1.0)
|
9
|
+
celluloid (0.16.0)
|
10
|
+
timers (~> 4.0.0)
|
11
|
+
coderay (1.1.0)
|
9
12
|
crack (0.3.2)
|
10
13
|
diff-lcs (1.2.1)
|
14
|
+
ffi (1.9.6)
|
15
|
+
formatador (0.2.5)
|
11
16
|
git (1.2.5)
|
17
|
+
guard (2.6.1)
|
18
|
+
formatador (>= 0.2.4)
|
19
|
+
listen (~> 2.7)
|
20
|
+
lumberjack (~> 1.0)
|
21
|
+
pry (>= 0.9.12)
|
22
|
+
thor (>= 0.18.1)
|
23
|
+
guard-rspec (3.1.0)
|
24
|
+
guard (>= 1.8)
|
25
|
+
rspec (~> 2.13)
|
26
|
+
hitimes (1.2.2)
|
12
27
|
httparty (0.10.2)
|
13
28
|
multi_json (~> 1.0)
|
14
29
|
multi_xml (>= 0.5.2)
|
@@ -19,9 +34,22 @@ GEM
|
|
19
34
|
rake
|
20
35
|
rdoc
|
21
36
|
json (1.7.7)
|
37
|
+
listen (2.7.11)
|
38
|
+
celluloid (>= 0.15.2)
|
39
|
+
rb-fsevent (>= 0.9.3)
|
40
|
+
rb-inotify (>= 0.9)
|
41
|
+
lumberjack (1.0.9)
|
42
|
+
method_source (0.8.2)
|
22
43
|
multi_json (1.6.1)
|
23
44
|
multi_xml (0.5.3)
|
45
|
+
pry (0.10.1)
|
46
|
+
coderay (~> 1.1.0)
|
47
|
+
method_source (~> 0.8.1)
|
48
|
+
slop (~> 3.4)
|
24
49
|
rake (10.0.3)
|
50
|
+
rb-fsevent (0.9.4)
|
51
|
+
rb-inotify (0.9.5)
|
52
|
+
ffi (>= 0.5.0)
|
25
53
|
rdoc (4.0.0)
|
26
54
|
json (~> 1.4)
|
27
55
|
rr (1.0.4)
|
@@ -29,7 +57,7 @@ GEM
|
|
29
57
|
rspec-core (~> 2.13.0)
|
30
58
|
rspec-expectations (~> 2.13.0)
|
31
59
|
rspec-mocks (~> 2.13.0)
|
32
|
-
rspec-core (2.13.
|
60
|
+
rspec-core (2.13.1)
|
33
61
|
rspec-expectations (2.13.0)
|
34
62
|
diff-lcs (>= 1.1.3, < 2.0)
|
35
63
|
rspec-mocks (2.13.0)
|
@@ -37,8 +65,13 @@ GEM
|
|
37
65
|
multi_json (~> 1.0)
|
38
66
|
simplecov-html (~> 0.7.1)
|
39
67
|
simplecov-html (0.7.1)
|
68
|
+
slop (3.6.0)
|
69
|
+
thor (0.19.1)
|
70
|
+
timecop (0.7.1)
|
71
|
+
timers (4.0.1)
|
72
|
+
hitimes
|
40
73
|
vcr (2.4.0)
|
41
|
-
webmock (1.
|
74
|
+
webmock (1.11.0)
|
42
75
|
addressable (>= 2.2.7)
|
43
76
|
crack (>= 0.3.2)
|
44
77
|
|
@@ -49,11 +82,13 @@ DEPENDENCIES
|
|
49
82
|
activesupport (>= 3.0.0)
|
50
83
|
awesome_print
|
51
84
|
bundler
|
85
|
+
guard-rspec
|
52
86
|
httparty (>= 0.10.0)
|
53
87
|
jeweler
|
54
88
|
rdoc
|
55
89
|
rr
|
56
90
|
rspec
|
57
91
|
simplecov
|
92
|
+
timecop
|
58
93
|
vcr
|
59
94
|
webmock
|
data/Guardfile
ADDED
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ Jeweler::Tasks.new do |gem|
|
|
20
20
|
#gem.license = "MIT"
|
21
21
|
gem.summary = %Q{hubspot-ruby is a wrapper for the HubSpot REST API}
|
22
22
|
gem.description = %Q{hubspot-ruby is a wrapper for the HubSpot REST API}
|
23
|
-
gem.email = "
|
23
|
+
gem.email = "hello@omadahealth.com"
|
24
24
|
gem.authors = ["Andrew DiMichele"]
|
25
25
|
gem.version = Hubspot::Version::STRING
|
26
26
|
|
data/hubspot-ruby.gemspec
CHANGED
@@ -2,16 +2,18 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: hubspot-ruby 0.1.3 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
8
|
s.name = "hubspot-ruby"
|
8
|
-
s.version = "0.1.
|
9
|
+
s.version = "0.1.3"
|
9
10
|
|
10
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
11
13
|
s.authors = ["Andrew DiMichele"]
|
12
|
-
s.date = "
|
14
|
+
s.date = "2014-10-27"
|
13
15
|
s.description = "hubspot-ruby is a wrapper for the HubSpot REST API"
|
14
|
-
s.email = "
|
16
|
+
s.email = "hello@omadahealth.com"
|
15
17
|
s.extra_rdoc_files = [
|
16
18
|
"LICENSE.txt",
|
17
19
|
"README.md"
|
@@ -21,17 +23,23 @@ Gem::Specification.new do |s|
|
|
21
23
|
".rspec",
|
22
24
|
"Gemfile",
|
23
25
|
"Gemfile.lock",
|
26
|
+
"Guardfile",
|
24
27
|
"LICENSE.txt",
|
25
28
|
"README.md",
|
26
29
|
"Rakefile",
|
27
30
|
"hubspot-ruby.gemspec",
|
28
31
|
"lib/hubspot-ruby.rb",
|
32
|
+
"lib/hubspot/blog.rb",
|
29
33
|
"lib/hubspot/config.rb",
|
30
34
|
"lib/hubspot/contact.rb",
|
31
35
|
"lib/hubspot/exceptions.rb",
|
32
36
|
"lib/hubspot/form.rb",
|
37
|
+
"lib/hubspot/topic.rb",
|
33
38
|
"lib/hubspot/utils.rb",
|
34
39
|
"lib/hubspot/version.rb",
|
40
|
+
"spec/fixtures/vcr_cassettes/blog_list.yml",
|
41
|
+
"spec/fixtures/vcr_cassettes/blog_posts.yml",
|
42
|
+
"spec/fixtures/vcr_cassettes/blog_posts_list.yml",
|
35
43
|
"spec/fixtures/vcr_cassettes/contact_create.yml",
|
36
44
|
"spec/fixtures/vcr_cassettes/contact_create_existing_email.yml",
|
37
45
|
"spec/fixtures/vcr_cassettes/contact_create_invalid_email.yml",
|
@@ -42,22 +50,28 @@ Gem::Specification.new do |s|
|
|
42
50
|
"spec/fixtures/vcr_cassettes/contact_find_by_id.yml",
|
43
51
|
"spec/fixtures/vcr_cassettes/contact_find_by_utk.yml",
|
44
52
|
"spec/fixtures/vcr_cassettes/contact_update.yml",
|
53
|
+
"spec/fixtures/vcr_cassettes/form_post.yml",
|
54
|
+
"spec/fixtures/vcr_cassettes/one_month_blog_posts_filter_state.yml",
|
55
|
+
"spec/fixtures/vcr_cassettes/one_month_blog_posts_list.yml",
|
56
|
+
"spec/fixtures/vcr_cassettes/topic_list.yml",
|
57
|
+
"spec/fixtures/vcr_cassettes/topics_list.yml",
|
45
58
|
"spec/lib/hubspot-ruby_spec.rb",
|
59
|
+
"spec/lib/hubspot/blog_spec.rb",
|
46
60
|
"spec/lib/hubspot/config_spec.rb",
|
47
61
|
"spec/lib/hubspot/contact_spec.rb",
|
48
62
|
"spec/lib/hubspot/form_spec.rb",
|
63
|
+
"spec/lib/hubspot/topic_spec.rb",
|
49
64
|
"spec/lib/hubspot/utils_spec.rb",
|
50
65
|
"spec/live/contacts_integration_spec.rb",
|
51
66
|
"spec/spec_helper.rb",
|
52
67
|
"spec/support/cassette_helper.rb"
|
53
68
|
]
|
54
69
|
s.homepage = "http://github.com/omadahealth/hubspot-ruby"
|
55
|
-
s.
|
56
|
-
s.rubygems_version = "1.8.25"
|
70
|
+
s.rubygems_version = "2.2.2"
|
57
71
|
s.summary = "hubspot-ruby is a wrapper for the HubSpot REST API"
|
58
72
|
|
59
73
|
if s.respond_to? :specification_version then
|
60
|
-
s.specification_version =
|
74
|
+
s.specification_version = 4
|
61
75
|
|
62
76
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
63
77
|
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0"])
|
@@ -71,6 +85,8 @@ Gem::Specification.new do |s|
|
|
71
85
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
72
86
|
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
73
87
|
s.add_development_dependency(%q<awesome_print>, [">= 0"])
|
88
|
+
s.add_development_dependency(%q<timecop>, [">= 0"])
|
89
|
+
s.add_development_dependency(%q<guard-rspec>, [">= 0"])
|
74
90
|
else
|
75
91
|
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
76
92
|
s.add_dependency(%q<httparty>, [">= 0.10.0"])
|
@@ -83,6 +99,8 @@ Gem::Specification.new do |s|
|
|
83
99
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
84
100
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
85
101
|
s.add_dependency(%q<awesome_print>, [">= 0"])
|
102
|
+
s.add_dependency(%q<timecop>, [">= 0"])
|
103
|
+
s.add_dependency(%q<guard-rspec>, [">= 0"])
|
86
104
|
end
|
87
105
|
else
|
88
106
|
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
@@ -96,6 +114,8 @@ Gem::Specification.new do |s|
|
|
96
114
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
97
115
|
s.add_dependency(%q<simplecov>, [">= 0"])
|
98
116
|
s.add_dependency(%q<awesome_print>, [">= 0"])
|
117
|
+
s.add_dependency(%q<timecop>, [">= 0"])
|
118
|
+
s.add_dependency(%q<guard-rspec>, [">= 0"])
|
99
119
|
end
|
100
120
|
end
|
101
121
|
|
data/lib/hubspot-ruby.rb
CHANGED
@@ -4,9 +4,11 @@ require 'hubspot/exceptions'
|
|
4
4
|
require 'hubspot/config'
|
5
5
|
require 'hubspot/contact'
|
6
6
|
require 'hubspot/form'
|
7
|
+
require 'hubspot/blog'
|
8
|
+
require 'hubspot/topic'
|
7
9
|
|
8
10
|
module Hubspot
|
9
11
|
def self.configure(config={})
|
10
12
|
Hubspot::Config.configure(config)
|
11
13
|
end
|
12
|
-
end
|
14
|
+
end
|
data/lib/hubspot/blog.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'hubspot/utils'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
module Hubspot
|
5
|
+
#
|
6
|
+
# HubSpot Contacts API
|
7
|
+
#
|
8
|
+
# {https://developers.hubspot.com/docs/endpoints#contacts-api}
|
9
|
+
#
|
10
|
+
class Blog
|
11
|
+
class InvalidParams < StandardError
|
12
|
+
end
|
13
|
+
|
14
|
+
BLOG_LIST_PATH = "/content/api/v2/blogs"
|
15
|
+
BLOG_POSTS_PATH = "/content/api/v2/blog-posts"
|
16
|
+
GET_BLOG_BY_ID_PATH = "/content/api/v2/blogs/:blog_id"
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# Lists the blogs
|
20
|
+
# {https://developers.hubspot.com/docs/methods/blogv2/get_blogs}
|
21
|
+
# No param filtering is currently implemented
|
22
|
+
# @return [Hubspot::Blog, []] the first 20 blogs or empty_array
|
23
|
+
def list
|
24
|
+
url = Hubspot::Utils.generate_url(BLOG_LIST_PATH)
|
25
|
+
resp = HTTParty.get(url, format: :json)
|
26
|
+
if resp.success?
|
27
|
+
resp.parsed_response['objects'].map do |blog_hash|
|
28
|
+
Blog.new(blog_hash)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
[]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Finds a specific blog by its ID
|
36
|
+
# {https://developers.hubspot.com/docs/methods/blogv2/get_blogs_blog_id}
|
37
|
+
# @return Hubspot::Blog or nil
|
38
|
+
def find_by_id(id)
|
39
|
+
url = Hubspot::Utils.generate_url(GET_BLOG_BY_ID_PATH, blog_id: id)
|
40
|
+
resp = HTTParty.get(url, format: :json)
|
41
|
+
if resp.success?
|
42
|
+
Blog.new(resp.parsed_response)
|
43
|
+
else
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_reader :properties
|
50
|
+
|
51
|
+
def initialize(response_hash)
|
52
|
+
@properties = response_hash #no need to parse anything, we have properties
|
53
|
+
end
|
54
|
+
|
55
|
+
def [](property)
|
56
|
+
@properties[property]
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# Returns the posts for this blog instance.
|
61
|
+
# defaults to returning the last 2 months worth of published blog posts
|
62
|
+
# in date descending order (i.e. most recent first)
|
63
|
+
# {https://developers.hubspot.com/docs/methods/blogv2/get_blog_posts}
|
64
|
+
# @return [Hubspot::BlogPost] or []
|
65
|
+
def posts(params = {})
|
66
|
+
default_params = {
|
67
|
+
content_group_id: self["id"],
|
68
|
+
order_by: '-created',
|
69
|
+
created__gt: Time.now - 2.month,
|
70
|
+
state: 'PUBLISHED'
|
71
|
+
}
|
72
|
+
raise InvalidParams.new('params must be passed as a hash') unless params.is_a?(Hash)
|
73
|
+
params = default_params.merge(params)
|
74
|
+
|
75
|
+
raise InvalidParams.new('State parameter was invalid') unless [false, 'PUBLISHED', 'DRAFT'].include?(params[:state])
|
76
|
+
params.each { |k, v| params.delete(k) if v == false }
|
77
|
+
|
78
|
+
url = Hubspot::Utils.generate_url(BLOG_POSTS_PATH, params)
|
79
|
+
|
80
|
+
resp = HTTParty.get(url, format: :json)
|
81
|
+
if resp.success?
|
82
|
+
blog_post_objects = resp.parsed_response['objects']
|
83
|
+
blog_post_objects.map do |blog_post_hash|
|
84
|
+
BlogPost.new(blog_post_hash)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
[]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class BlogPost
|
93
|
+
GET_BLOG_POST_BY_ID_PATH = "/content/api/v2/blog-posts/:blog_post_id"
|
94
|
+
|
95
|
+
# Returns a specific blog post by ID
|
96
|
+
# {https://developers.hubspot.com/docs/methods/blogv2/get_blog_posts_blog_post_id}
|
97
|
+
# @return [Hubspot::BlogPost] or nil
|
98
|
+
def self.find_by_blog_post_id(id)
|
99
|
+
url = Hubspot::Utils.generate_url(GET_BLOG_POST_BY_ID_PATH, blog_post_id: id)
|
100
|
+
resp = HTTParty.get(url, format: :json)
|
101
|
+
if resp.success?
|
102
|
+
BlogPost.new(resp.parsed_response)
|
103
|
+
else
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize(response_hash)
|
109
|
+
@properties = response_hash #no need to parse anything, we have properties
|
110
|
+
end
|
111
|
+
|
112
|
+
def [](property)
|
113
|
+
@properties[property]
|
114
|
+
end
|
115
|
+
|
116
|
+
def created_at
|
117
|
+
Time.at(@properties['created'] / 1000)
|
118
|
+
end
|
119
|
+
|
120
|
+
def topics
|
121
|
+
@topics ||= begin
|
122
|
+
if @properties['topic_ids'].empty?
|
123
|
+
[]
|
124
|
+
else
|
125
|
+
@properties['topic_ids'].map do |topic_id|
|
126
|
+
Hubspot::Topic.find_by_topic_id(topic_id)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
data/lib/hubspot/contact.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'hubspot/utils'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
module Hubspot
|
5
|
+
#
|
6
|
+
# HubSpot Topics API
|
7
|
+
#
|
8
|
+
class Topic
|
9
|
+
TOPIC_LIST_PATH = "/content/api/v2/topics"
|
10
|
+
GET_TOPIC_BY_ID_PATH = "/content/api/v2/topics/:topic_id"
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Lists the topics
|
14
|
+
# {https://developers.hubspot.com/docs/methods/blogv2/get_topics)
|
15
|
+
# @return [Hubspot::Topic, []] array of topics or empty_array
|
16
|
+
def list
|
17
|
+
url = Hubspot::Utils.generate_url(TOPIC_LIST_PATH)
|
18
|
+
resp = HTTParty.get(url, format: :json)
|
19
|
+
if resp.success?
|
20
|
+
resp.parsed_response['objects'].map do |topic_hash|
|
21
|
+
Topic.new(topic_hash)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
[]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Finds the details for a specific topic_id
|
29
|
+
# {https://developers.hubspot.com/docs/methods/blogv2/get_topics_topic_id }
|
30
|
+
# @return Hubspot::Topic or nil
|
31
|
+
|
32
|
+
def find_by_topic_id(id)
|
33
|
+
url = Hubspot::Utils.generate_url(GET_TOPIC_BY_ID_PATH, topic_id: id)
|
34
|
+
resp = HTTParty.get(url, format: :json)
|
35
|
+
if resp.success?
|
36
|
+
Topic.new(resp.parsed_response)
|
37
|
+
else
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :properties
|
44
|
+
|
45
|
+
def initialize(response_hash)
|
46
|
+
@properties = response_hash #no need to parse anything, we have properties
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](property)
|
50
|
+
@properties[property]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/hubspot/utils.rb
CHANGED
@@ -39,10 +39,30 @@ module Hubspot
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
raise(Hubspot::MissingInterpolation.new("Interpolation not resolved")) if path =~ /:/
|
42
|
-
query = params.map{ |k,v|
|
42
|
+
query = params.map{ |k,v| param_string(k,v) }.join("&")
|
43
43
|
path += "?" if query.present?
|
44
44
|
base_url + path + query
|
45
45
|
end
|
46
|
+
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def converted_value(value)
|
51
|
+
if (value.is_a?(Time))
|
52
|
+
(value.to_i * 1000) # convert into milliseconds since epoch
|
53
|
+
else
|
54
|
+
value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def param_string(key,value)
|
59
|
+
if (key =~ /range/)
|
60
|
+
raise "Value must be a range" unless value.is_a?(Range)
|
61
|
+
"#{key}=#{converted_value(value.begin)}&#{key}=#{converted_value(value.end)}"
|
62
|
+
else
|
63
|
+
"#{key}=#{converted_value(value)}"
|
64
|
+
end
|
65
|
+
end
|
46
66
|
end
|
47
67
|
end
|
48
|
-
end
|
68
|
+
end
|