readit 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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) [![Build Status](https://travis-ci.org/29decibel/readit.png)](https://travis-ci.org/29decibel/readit) [![Code Climate](https://codeclimate.com/badge.png)](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
|