readit 0.0.7 → 0.0.8
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.
- data/.travis.yml +12 -0
- data/Gemfile +3 -1
- data/Readme.md +61 -5
- data/features/bookmarks.feature +9 -9
- data/features/readability.yml.sample +6 -0
- data/features/step_definitions/api_steps.rb +4 -4
- data/lib/readit/version.rb +1 -1
- data/lib/readit.rb +89 -11
- data/spec/cases/api_spec.rb +29 -6
- data/spec/cases/parser_spec.rb +19 -0
- data/spec/cases/tags_spec.rb +46 -0
- data/spec/cassettes/Readit_API/can_add_tags_to_one_bookmark.yml +699 -0
- data/spec/cassettes/Readit_API/can_fetch_all_tags_information_of_current_user.yml +45 -0
- data/spec/cassettes/Readit_API/can_fetch_tags_of_one_bookmark.yml +687 -0
- data/spec/cassettes/Readit_API/can_get_bookmark_location_when_bookmarked_a_url.yml +52 -0
- data/spec/cassettes/Readit_API/can_get_meta_infos_of_bookmarks.yml +646 -0
- data/spec/cassettes/Readit_API/can_remove_tag_info_of_one_bookmark_by_tag_id.yml +786 -0
- data/spec/cassettes/Readit_API/should_add_bookmark.yml +52 -0
- data/spec/cassettes/Readit_API/should_get_bookmarks_according_to_added_since.yml +86 -0
- data/spec/cassettes/Readit_API/should_get_the_article_content.yml +322 -0
- data/spec/cassettes/Readit_API/should_get_the_bookmark_info_by_bookmark_id.yml +713 -0
- data/spec/cassettes/Readit_API/should_get_user_infos.yml +51 -0
- data/spec/cassettes/Readit_API/should_get_user_s_bookmarks.yml +634 -0
- data/spec/cassettes/Readit_API/should_provide_the_bookmark_id_of_an_already_bookmarked_url.yml +683 -0
- data/spec/cassettes/Readit_API/should_update_bookmark_to_archive.yml +808 -0
- data/spec/cassettes/Readit_API/should_update_bookmark_to_favarite.yml +808 -0
- data/spec/cassettes/Readit_Parser/should_return_parsed_article_cotents.yml +638 -0
- data/spec/readability.yml.sample +7 -0
- data/spec/spec_helper.rb +28 -1
- metadata +65 -11
- data/readability.yml.sample +0 -3
data/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
before_install:
|
2
|
+
- gem install bundler
|
3
|
+
branches:
|
4
|
+
only:
|
5
|
+
- 'master'
|
6
|
+
rvm:
|
7
|
+
- 1.9.2
|
8
|
+
- 1.9.3
|
9
|
+
before_script:
|
10
|
+
- "cp spec/readability.yml.sample spec/readability.yml"
|
11
|
+
- "cp features/readability.yml.sample features/readability.yml"
|
12
|
+
script: "bundle exec rspec"
|
data/Gemfile
CHANGED
@@ -7,11 +7,13 @@ group :development, :test do
|
|
7
7
|
gem 'guard'
|
8
8
|
gem 'guard-rspec'
|
9
9
|
gem 'cucumber'
|
10
|
+
gem 'vcr','2.0.1'
|
11
|
+
gem 'webmock','~> 1.8.0', :require => false
|
10
12
|
|
11
13
|
if RUBY_PLATFORM =~ /darwin/
|
12
14
|
# OS X integration
|
13
15
|
gem "ruby_gntp"
|
14
|
-
gem "rb-fsevent", "~> 0.
|
16
|
+
gem "rb-fsevent", "~> 0.9.0"
|
15
17
|
end
|
16
18
|
end
|
17
19
|
gem 'oauth'
|
data/Readme.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
## Simple api client of [
|
1
|
+
## Simple api client of [Readability](http://readability.com) [](https://travis-ci.org/29decibel/readit) [](https://codeclimate.com/github/29decibel/readit)
|
2
2
|
|
3
3
|
### Installation
|
4
4
|
```ruby
|
@@ -34,19 +34,75 @@ Readit::Config.consumer_secret = some_value
|
|
34
34
|
```
|
35
35
|
|
36
36
|
### API usage
|
37
|
-
|
37
|
+
|
38
|
+
#### Initialize api client
|
39
|
+
``` ruby
|
38
40
|
|
39
41
|
@api = Readit::API.new 'access_token','access_token_secret'
|
42
|
+
```
|
40
43
|
|
44
|
+
#### User info
|
45
|
+
```ruby
|
41
46
|
# get user info
|
42
47
|
@api.me
|
48
|
+
```
|
49
|
+
|
50
|
+
#### Bookmarks
|
51
|
+
```ruby
|
43
52
|
# get all bookmarks, result will be a hash array
|
44
53
|
@api.bookmarks
|
45
|
-
# get
|
46
|
-
|
54
|
+
# get bookmarks along with meta info :
|
55
|
+
# item_count, item_count_total, num_pages, page
|
56
|
+
bookmarks,meta = @api.bookmarks(:include_meta => true)
|
57
|
+
|
47
58
|
# add bookmark
|
48
|
-
@api.bookmark
|
59
|
+
bookmark_info = @api.bookmark(:url=>'http://some_article_url.html')
|
60
|
+
# check bookmarked infos
|
61
|
+
# bookmark_info.bookmark_id
|
62
|
+
# bookmark_info.article_id
|
63
|
+
|
49
64
|
# get bookmark by bookmark_id
|
50
65
|
@api.bookmarks :bookmark_id => some_bookmark_id
|
66
|
+
|
67
|
+
# archive a bookmark
|
68
|
+
@api.archive some_bookmark_id
|
69
|
+
|
70
|
+
# favorite a bookmark
|
71
|
+
@api.favorite some_bookmark_id
|
72
|
+
|
73
|
+
# or you can just call update_bookmark to
|
74
|
+
# update a bookmark to favorited or archived
|
75
|
+
@api.update_bookmark bookmark_id,:favorite => 1,:archive => 0
|
76
|
+
```
|
77
|
+
|
78
|
+
#### Tags
|
79
|
+
```ruby
|
80
|
+
# get all tags of current user
|
81
|
+
@api.all_tags # [#<Hashie::Mash id=39086 text="rails">, #<Hashie::Mash id=39085 text="ruby">, #<Hashie::Mash id=39087 text="tag3">]
|
82
|
+
|
83
|
+
# add tags to one bookmark
|
84
|
+
@api.add_tags bookmark_id, "tag1,tag2,tag3"
|
85
|
+
|
86
|
+
# fetch all tags of one bookmark
|
87
|
+
@api.tags bookmark_id
|
88
|
+
|
89
|
+
# remove tag from one bookmark
|
90
|
+
@api.remove_tag bookmark_id, tag_id
|
91
|
+
```
|
92
|
+
|
93
|
+
### Parser
|
94
|
+
```ruby
|
95
|
+
@parser = Readit::Parser.new "some_parser_token"
|
96
|
+
@parser.parse some_url
|
51
97
|
```
|
52
98
|
|
99
|
+
#### Get Article
|
100
|
+
```ruby
|
101
|
+
# get one artile by article_id
|
102
|
+
@api.article 'article_id'
|
103
|
+
|
104
|
+
```
|
105
|
+
|
106
|
+
### At last but not least
|
107
|
+
>Contributions are welcome!
|
108
|
+
|
data/features/bookmarks.feature
CHANGED
@@ -38,15 +38,15 @@ Scenario: archive bookmark by id
|
|
38
38
|
When call archive with bookmark_id
|
39
39
|
Then got bookmark's archive set to true
|
40
40
|
|
41
|
-
Scenario: user can add and delete bookmark
|
42
|
-
Given the readit api client
|
43
|
-
When add bookmark with url http://google.com
|
44
|
-
Then got status ok
|
45
|
-
When fetch the
|
46
|
-
Then got the bookmark with url http://google.com
|
47
|
-
When delete this new added bookmark
|
48
|
-
And fetch the latest bookmark
|
49
|
-
Then the latesed bookmark which url is not http://google.com
|
41
|
+
## Scenario: user can add and delete bookmark
|
42
|
+
## Given the readit api client
|
43
|
+
## When add bookmark with url http://google.com
|
44
|
+
## Then got status ok
|
45
|
+
## When fetch the bookmark info with url http://google.com
|
46
|
+
## Then got the bookmark with url http://google.com
|
47
|
+
## When delete this new added bookmark
|
48
|
+
## And fetch the latest bookmark
|
49
|
+
## Then the latesed bookmark which url is not http://google.com
|
50
50
|
|
51
51
|
|
52
52
|
|
@@ -19,10 +19,10 @@ Then /^get the api client$/ do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
Given /^the readit api client$/ do
|
22
|
-
|
23
|
-
Readit::Config.consumer_key =
|
24
|
-
Readit::Config.consumer_secret =
|
25
|
-
@client = Readit::API.new
|
22
|
+
tokens = YAML.load_file(File.join(File.dirname(__FILE__),'../readability.yml'))["development"]
|
23
|
+
Readit::Config.consumer_key = tokens['consumer_key']
|
24
|
+
Readit::Config.consumer_secret = tokens['consumer_secret']
|
25
|
+
@client = Readit::API.new tokens['oauth_token'],tokens['oauth_token_secret']
|
26
26
|
end
|
27
27
|
|
28
28
|
|
data/lib/readit/version.rb
CHANGED
data/lib/readit.rb
CHANGED
@@ -3,11 +3,14 @@ require 'multi_json'
|
|
3
3
|
require 'oauth'
|
4
4
|
require 'uri'
|
5
5
|
require 'hashie'
|
6
|
+
require 'net/http'
|
6
7
|
# plugin railtie hook when using rails
|
7
8
|
require 'readit/railtie' if defined?(Rails)
|
8
9
|
|
9
10
|
module Readit
|
10
11
|
|
12
|
+
SITE_URL = 'https://www.readability.com/'
|
13
|
+
|
11
14
|
class ReaditError < StandardError;end
|
12
15
|
|
13
16
|
module Config
|
@@ -28,6 +31,34 @@ module Readit
|
|
28
31
|
@@consumer_secret = val
|
29
32
|
end
|
30
33
|
|
34
|
+
def self.parser_token
|
35
|
+
@@parser_token
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.parser_token=(val)
|
39
|
+
@@parser_token = val
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class Parser
|
45
|
+
def initialize(parser_token = Readit::Config.parser_token)
|
46
|
+
@parser_token = parser_token
|
47
|
+
raise ReaditError.new('please set parser token before use') unless @parser_token
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse(url)
|
51
|
+
uri = URI.parse("#{SITE_URL}api/content/v1/parser?token=#{@parser_token}&url=#{URI.escape(url)}")
|
52
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
53
|
+
# using https
|
54
|
+
http.use_ssl = true
|
55
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
56
|
+
# create request
|
57
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
58
|
+
response = http.request(request)
|
59
|
+
|
60
|
+
Hashie::Mash.new MultiJson.decode(response.body)
|
61
|
+
end
|
31
62
|
end
|
32
63
|
|
33
64
|
class API
|
@@ -47,16 +78,14 @@ module Readit
|
|
47
78
|
|
48
79
|
attr_reader :access_token
|
49
80
|
|
50
|
-
SITE_URL = 'https://www.readability.com/'
|
51
|
-
|
52
81
|
# Retrieve the base API URI - information about subresources.
|
53
82
|
def resource_info
|
54
83
|
request(:get,'/')
|
55
84
|
end
|
56
85
|
|
57
|
-
# Retrieve a single Article, including its content.
|
86
|
+
# Retrieve a single Article, including its content.
|
58
87
|
# api rest address: /articles/{article_id}
|
59
|
-
# @param article_id the article_id
|
88
|
+
# @param article_id the article_id
|
60
89
|
def article(article_id)
|
61
90
|
request(:get,"/articles/#{article_id}")
|
62
91
|
end
|
@@ -83,23 +112,40 @@ module Readit
|
|
83
112
|
# per_page default 20,max 50
|
84
113
|
# exclude_accessibility
|
85
114
|
# only_deleted
|
115
|
+
# ***** Special args *****
|
116
|
+
# to get bookmarks meta infos
|
117
|
+
# :include_meta => true
|
118
|
+
# item_count=20 item_count_total=633 num_pages=32 page=1
|
119
|
+
# bookmarks,meta = @api.bookmarks(:include_meta => true)
|
86
120
|
def bookmarks(args={})
|
87
121
|
if args[:bookmark_id] and args[:bookmark_id]!=''
|
88
122
|
request(:get,"/bookmarks/#{args[:bookmark_id]}")
|
89
123
|
else
|
90
124
|
params = args.map{|k,v| "#{k}=#{v}"}.join('&')
|
91
|
-
request(:get,"/bookmarks?#{URI.escape(params)}")
|
125
|
+
result = request(:get,"/bookmarks?#{URI.escape(params)}")
|
126
|
+
args[:include_meta] ? [result.bookmarks,result.meta] : result.bookmarks
|
92
127
|
end
|
93
128
|
end
|
94
129
|
|
95
|
-
# Add a bookmark. Returns 202 Accepted, meaning that the bookmark has been added but no guarantees are made as
|
130
|
+
# Add a bookmark. Returns 202 Accepted, meaning that the bookmark has been added but no guarantees are made as
|
96
131
|
# to whether the article proper has yet been parsed.
|
97
132
|
# @param args args to bookmark a url
|
98
133
|
# url the address to bookmark
|
99
134
|
# favorite 0 or 1
|
100
135
|
# archive 0 or 1
|
136
|
+
# Return example
|
137
|
+
# success:
|
138
|
+
# {:status => '202',:bookmark_id => '233444', :article_id => 't323r2'}
|
139
|
+
# conflicts
|
140
|
+
# {:status => '409'}
|
101
141
|
def bookmark(args={})
|
102
|
-
|
142
|
+
raise ReaditError.new('expect at lease a hash argument with key :url') unless args[:url]
|
143
|
+
request(:post,'/bookmarks',args) do |response|
|
144
|
+
Hashie::Mash.new({
|
145
|
+
:status =>response.code,
|
146
|
+
:bookmark_id=> ['202', '409'].include?(response.code) ? response["Location"].match(/bookmarks\/(.*)/)[1] : '',
|
147
|
+
:article_id=> response.code== '202' ? response["X-Article-Location"].match(/articles\/(.*)/)[1] : ''})
|
148
|
+
end
|
103
149
|
end
|
104
150
|
|
105
151
|
# Update a bookmark. Returns 200 on successful update.
|
@@ -124,11 +170,11 @@ module Readit
|
|
124
170
|
update_bookmark(bookmark_id,:favorite=>1)
|
125
171
|
end
|
126
172
|
|
127
|
-
# Remove a single bookmark from this user's history.
|
173
|
+
# Remove a single bookmark from this user's history.
|
128
174
|
# NOTE: THIS IS PROBABLY NOT WHAT YOU WANT. This is particularly for the case where a user accidentally bookmarks
|
129
|
-
# something they have no intention of reading or supporting.
|
175
|
+
# something they have no intention of reading or supporting.
|
130
176
|
# In almost all cases, you'll probably want to use archive by POSTing archive=1 to this bookmark.
|
131
|
-
# If you use DELETE and this months bookmarks have not yet been tallied,
|
177
|
+
# If you use DELETE and this months bookmarks have not yet been tallied,
|
132
178
|
# the site associated with this bookmark will not receive any contributions for this bookmark.
|
133
179
|
# Use archive! It's better.
|
134
180
|
# Returns a 204 on successful remove.
|
@@ -149,12 +195,44 @@ module Readit
|
|
149
195
|
request(:get,"/users/_current")
|
150
196
|
end
|
151
197
|
|
152
|
-
|
198
|
+
# Retrieve all tags of current user
|
199
|
+
# api rest address :/tags
|
200
|
+
def all_tags
|
201
|
+
request(:get,"/tags")
|
202
|
+
end
|
203
|
+
|
204
|
+
# add tags to one bookmark
|
205
|
+
# api rest address POST :/bookmarks/{bookmark_id}/tags
|
206
|
+
def add_tags(bookmark_id, tags_string)
|
207
|
+
request(:post, "/bookmarks/#{bookmark_id}/tags",{ :tags => tags_string }).tags
|
208
|
+
end
|
209
|
+
|
210
|
+
# Retrieve all tags of one bookmark
|
211
|
+
# api rest address GET :/bookmarks/{bookmark_id}/tags
|
212
|
+
def tags(bookmark_id)
|
213
|
+
request(:get, "/bookmarks/#{bookmark_id}/tags").tags
|
214
|
+
end
|
215
|
+
|
216
|
+
# remove tag info from bookmark
|
217
|
+
# api rest address DELETE /bookmarks/{bookmark_id}/tags/{tag_id}
|
218
|
+
def remove_tag(bookmark_id, tag_id)
|
219
|
+
request(:delete, "/bookmarks/#{bookmark_id}/tags/#{tag_id}")
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
153
223
|
def request(method,url,args={})
|
154
224
|
consumer = ::OAuth::Consumer.new(Readit::Config.consumer_key,Readit::Config.consumer_secret,:site=>SITE_URL)
|
155
225
|
atoken = ::OAuth::AccessToken.new(consumer, @access_token, @access_token_secret)
|
156
226
|
#response = client.send(method,"/api/rest/v1#{url}",args.merge!('oauth_token'=>@access_token,'oauth_token_secret'=>'5VEnMNPr7Q4393wxAYdnTWnpWwn7bHm4','oauth_consumer_key'=>'lidongbin','oauth_consumer_secret'=>'gvjSYqH4PLWQtQG8Ywk7wKZnEgd4xf2C'))
|
157
227
|
response = atoken.send(method,"/api/rest/v1#{url}",args)
|
228
|
+
if block_given?
|
229
|
+
yield response
|
230
|
+
else
|
231
|
+
hashie_response(response)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def hashie_response(response)
|
158
236
|
if response.body==nil or response.body==''
|
159
237
|
Hashie::Mash.new({:status => response.code})
|
160
238
|
else
|
data/spec/cases/api_spec.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'time'
|
3
3
|
|
4
|
-
describe "Readit::API" do
|
4
|
+
describe "Readit::API",:vcr do
|
5
5
|
before do
|
6
6
|
# load consumer infos
|
7
|
-
|
8
|
-
Readit::Config.consumer_key =
|
9
|
-
Readit::Config.consumer_secret =
|
10
|
-
@api = Readit::API.new '
|
7
|
+
tokens = YAML.load_file(File.join(File.dirname(__FILE__),'../readability.yml'))["development"]
|
8
|
+
Readit::Config.consumer_key = tokens['consumer_key']
|
9
|
+
Readit::Config.consumer_secret = tokens['consumer_secret']
|
10
|
+
@api = Readit::API.new tokens['oauth_token'],tokens['oauth_token_secret']
|
11
11
|
end
|
12
12
|
|
13
13
|
let :bookmarks do
|
14
|
-
@bms
|
14
|
+
@bms = @api.bookmarks
|
15
15
|
end
|
16
16
|
|
17
17
|
let :bookmark_ids do
|
@@ -33,6 +33,22 @@ describe "Readit::API" do
|
|
33
33
|
resp.should_not == nil
|
34
34
|
end
|
35
35
|
|
36
|
+
it "can get meta infos of bookmarks" do
|
37
|
+
bookmarks,meta = @api.bookmarks(:include_meta => true)
|
38
|
+
bookmarks.count.should > 0
|
39
|
+
meta.item_count.should > 0
|
40
|
+
meta.item_count_total.should > 0
|
41
|
+
meta.num_pages.should > 0
|
42
|
+
meta.page.should > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
it "can get bookmark location when bookmarked a url" do
|
46
|
+
url = 'http://www.jslib.org.cn/njlib_xsyj/201011/t20101130_98154.htm'
|
47
|
+
resp = @api.bookmark :url => url
|
48
|
+
resp.bookmark_id.should_not be_nil
|
49
|
+
resp.article_id.should_not be_nil
|
50
|
+
end
|
51
|
+
|
36
52
|
it "should get the article content" do
|
37
53
|
article = @api.article 'eg60dxbv'
|
38
54
|
#puts article
|
@@ -64,6 +80,13 @@ describe "Readit::API" do
|
|
64
80
|
bookmark.should be_archive
|
65
81
|
end
|
66
82
|
|
83
|
+
it "should raise exception when call bookmark without url provide" do
|
84
|
+
lambda { @api.bookmark }.should raise_error
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should provide the bookmark_id of an already bookmarked url" do
|
88
|
+
@api.bookmark(:url => @api.bookmarks.first.article.url).bookmark_id.should_not be_empty
|
89
|
+
end
|
67
90
|
|
68
91
|
end
|
69
92
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
describe "Readit::Parser",:vcr do
|
5
|
+
before do
|
6
|
+
# load consumer infos
|
7
|
+
tokens = YAML.load_file(File.join(File.dirname(__FILE__),'../readability.yml'))["development"]
|
8
|
+
Readit::Config.parser_token = tokens['parser_token']
|
9
|
+
@parser = Readit::Parser.new tokens['parser_token']
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return parsed article cotents" do
|
13
|
+
url = "http://statico.github.com/vim.html"
|
14
|
+
article = @parser.parse url
|
15
|
+
article.title.should_not == nil
|
16
|
+
article.content.should_not == nil
|
17
|
+
article.content.length.should > 0
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
describe "Readit::API",:vcr do
|
5
|
+
before do
|
6
|
+
# load consumer infos
|
7
|
+
tokens = YAML.load_file(File.join(File.dirname(__FILE__),'../readability.yml'))["development"]
|
8
|
+
Readit::Config.consumer_key = tokens['consumer_key']
|
9
|
+
Readit::Config.consumer_secret = tokens['consumer_secret']
|
10
|
+
@api = Readit::API.new tokens['oauth_token'],tokens['oauth_token_secret']
|
11
|
+
end
|
12
|
+
|
13
|
+
let :bookmarks do
|
14
|
+
@bms = @api.bookmarks
|
15
|
+
end
|
16
|
+
|
17
|
+
let :bookmark_ids do
|
18
|
+
bookmarks.map{|a| a['id']}
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can fetch all tags information of current user" do
|
22
|
+
tags = @api.all_tags
|
23
|
+
tags.count.should > 0
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can add tags to one bookmark" do
|
27
|
+
bookmark_id = bookmark_ids.first
|
28
|
+
tags = @api.add_tags bookmark_id, "book,movie,music"
|
29
|
+
tags.count.should == 3
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can fetch tags of one bookmark" do
|
33
|
+
bookmark_id = bookmark_ids.first
|
34
|
+
tags = @api.tags bookmark_id
|
35
|
+
tags.count.should == 3
|
36
|
+
end
|
37
|
+
|
38
|
+
it "can remove tag info of one bookmark by tag id" do
|
39
|
+
bookmark_id = bookmark_ids.first
|
40
|
+
tags = @api.tags bookmark_id
|
41
|
+
@api.remove_tag bookmark_id,tags.first.id
|
42
|
+
@api.tags(bookmark_id).count.should == 2
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|