etwin 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +6 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +82 -0
- data/README.md +30 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/etwin.gemspec +37 -0
- data/lib/etwin.rb +27 -0
- data/lib/etwin/_auth.rb +4 -0
- data/lib/etwin/auth.rb +14 -0
- data/lib/etwin/auth/_auth_context.rb +30 -0
- data/lib/etwin/auth/auth_context.rb +9 -0
- data/lib/etwin/auth/auth_scope.rb +25 -0
- data/lib/etwin/auth/auth_type.rb +26 -0
- data/lib/etwin/auth/guest_auth_context.rb +71 -0
- data/lib/etwin/auth/user_auth_context.rb +83 -0
- data/lib/etwin/client.rb +11 -0
- data/lib/etwin/client/auth.rb +66 -0
- data/lib/etwin/client/etwin_client.rb +17 -0
- data/lib/etwin/client/http_etwin_client.rb +36 -0
- data/lib/etwin/core.rb +10 -0
- data/lib/etwin/core/object_type.rb +35 -0
- data/lib/etwin/hammerfest.rb +13 -0
- data/lib/etwin/hammerfest/hammerfest_server.rb +27 -0
- data/lib/etwin/hammerfest/hammerfest_user_id.rb +57 -0
- data/lib/etwin/hammerfest/hammerfest_username.rb +57 -0
- data/lib/etwin/hammerfest/short_hammerfest_user.rb +103 -0
- data/lib/etwin/link.rb +15 -0
- data/lib/etwin/link/hammerfest_link.rb +106 -0
- data/lib/etwin/link/link_action.rb +93 -0
- data/lib/etwin/link/twinoid_link.rb +106 -0
- data/lib/etwin/link/versioned_hammerfest_link.rb +93 -0
- data/lib/etwin/link/versioned_links.rb +123 -0
- data/lib/etwin/link/versioned_twinoid_link.rb +93 -0
- data/lib/etwin/twinoid.rb +12 -0
- data/lib/etwin/twinoid/short_twinoid_user.rb +93 -0
- data/lib/etwin/twinoid/twinoid_user_display_name.rb +57 -0
- data/lib/etwin/twinoid/twinoid_user_id.rb +57 -0
- data/lib/etwin/user.rb +15 -0
- data/lib/etwin/user/short_user.rb +93 -0
- data/lib/etwin/user/user.rb +123 -0
- data/lib/etwin/user/user_display_name.rb +57 -0
- data/lib/etwin/user/user_display_name_version.rb +83 -0
- data/lib/etwin/user/user_display_name_versions.rb +83 -0
- data/lib/etwin/user/user_id.rb +57 -0
- data/lib/etwin/version.rb +6 -0
- data/sorbet/config +2 -0
- data/sorbet/rbi/gems/ast.rbi +48 -0
- data/sorbet/rbi/gems/debase.rbi +67 -0
- data/sorbet/rbi/gems/irb.rbi +352 -0
- data/sorbet/rbi/gems/parallel.rbi +83 -0
- data/sorbet/rbi/gems/parser.rbi +1405 -0
- data/sorbet/rbi/gems/rainbow.rbi +118 -0
- data/sorbet/rbi/gems/rake.rbi +644 -0
- data/sorbet/rbi/gems/regexp_parser.rbi +914 -0
- data/sorbet/rbi/gems/reline.rbi +556 -0
- data/sorbet/rbi/gems/rexml.rbi +605 -0
- data/sorbet/rbi/gems/rspec-core.rbi +1920 -0
- data/sorbet/rbi/gems/rspec-expectations.rbi +1148 -0
- data/sorbet/rbi/gems/rspec-mocks.rbi +1090 -0
- data/sorbet/rbi/gems/rspec-support.rbi +280 -0
- data/sorbet/rbi/gems/rspec.rbi +15 -0
- data/sorbet/rbi/gems/rubocop-ast.rbi +1338 -0
- data/sorbet/rbi/gems/rubocop.rbi +7491 -0
- data/sorbet/rbi/gems/ruby-debug-ide.rbi +608 -0
- data/sorbet/rbi/gems/ruby-progressbar.rbi +305 -0
- data/sorbet/rbi/gems/unicode-display_width.rbi +17 -0
- data/sorbet/rbi/hidden-definitions/errors.txt +4905 -0
- data/sorbet/rbi/hidden-definitions/hidden.rbi +9193 -0
- data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +276 -0
- data/sorbet/rbi/todo.rbi +7 -0
- metadata +220 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Auth
|
6
|
+
# User authentication context
|
7
|
+
class UserAuthContext
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
final!
|
12
|
+
|
13
|
+
sig(:final) { returns(AuthScope) }
|
14
|
+
attr_reader :scope
|
15
|
+
|
16
|
+
sig(:final) { returns(Etwin::User::ShortUser) }
|
17
|
+
attr_reader :user
|
18
|
+
|
19
|
+
sig(:final) { returns(T::Boolean) }
|
20
|
+
attr_reader :is_administrator
|
21
|
+
|
22
|
+
sig(:final) { params(scope: AuthScope, user: Etwin::User::ShortUser, is_administrator: T::Boolean).void }
|
23
|
+
def initialize(scope, user, is_administrator)
|
24
|
+
@scope = T.let(scope, AuthScope)
|
25
|
+
@user = T.let(user, Etwin::User::ShortUser)
|
26
|
+
@is_administrator = T.let(is_administrator, T::Boolean)
|
27
|
+
freeze
|
28
|
+
end
|
29
|
+
|
30
|
+
sig(:final) { params(other: BasicObject).returns(T::Boolean) }
|
31
|
+
def ==(other)
|
32
|
+
case other
|
33
|
+
when UserAuthContext
|
34
|
+
@scope == other.scope && @user == other.user && @is_administrator == other.is_administrator
|
35
|
+
else
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
sig(:final) { returns(Integer) }
|
41
|
+
def hash
|
42
|
+
[@scope, @user, @is_administrator].hash
|
43
|
+
end
|
44
|
+
|
45
|
+
# https://github.com/sorbet/sorbet/blob/master/rbi/stdlib/json.rbi#L194
|
46
|
+
sig(:final) { params(opts: T.untyped).returns(String) }
|
47
|
+
def to_json(opts = nil)
|
48
|
+
JSON.generate(as_json, opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
sig(:final) { returns(T::Hash[String, T.untyped]) }
|
52
|
+
def as_json
|
53
|
+
{
|
54
|
+
'scope' => @scope.serialize,
|
55
|
+
'user' => @user.as_json,
|
56
|
+
'is_administrator' => @is_administrator
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
sig(:final) { returns(String) }
|
61
|
+
def inspect
|
62
|
+
"UserAuthContext(scope=#{@scope.inspect},user=#{@user.inspect},is_administrator=#{@is_administrator.inspect})"
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
extend T::Sig
|
67
|
+
|
68
|
+
sig(:final) { params(json_str: String).returns(T.attached_class) }
|
69
|
+
def from_json(json_str)
|
70
|
+
deserialize JSON.parse(json_str)
|
71
|
+
end
|
72
|
+
|
73
|
+
sig(:final) { params(raw: T.untyped).returns(T.attached_class) }
|
74
|
+
def deserialize(raw)
|
75
|
+
scope = AuthScope.deserialize(raw['scope'])
|
76
|
+
user = Etwin::User::ShortUser.deserialize(raw['user'])
|
77
|
+
is_administrator = raw['is_administrator']
|
78
|
+
new(scope, user, is_administrator)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/etwin/client.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Client
|
6
|
+
# Eternal-Twin client authentication data
|
7
|
+
class Auth
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
final!
|
12
|
+
|
13
|
+
sig(:final) { returns(T.nilable(String)) }
|
14
|
+
attr_reader :authorization_header
|
15
|
+
|
16
|
+
sig(:final) { params(authorization_header: T.nilable(String)).void }
|
17
|
+
def initialize(authorization_header)
|
18
|
+
@authorization_header = T.let(authorization_header, T.nilable(String))
|
19
|
+
freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
sig(:final) { params(other: BasicObject).returns(T::Boolean) }
|
23
|
+
def ==(other)
|
24
|
+
case other
|
25
|
+
when Auth
|
26
|
+
@authorization_header == other.authorization_header
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
sig(:final) { returns(Integer) }
|
33
|
+
def hash
|
34
|
+
[@authorization_header].hash
|
35
|
+
end
|
36
|
+
|
37
|
+
sig(:final) { returns(String) }
|
38
|
+
def inspect
|
39
|
+
PP.singleline_pp(self, String.new)
|
40
|
+
end
|
41
|
+
|
42
|
+
sig(:final) { params(pp: T.untyped).returns(T.untyped) }
|
43
|
+
def pretty_print(pp)
|
44
|
+
pp.group(0, "#{self.class.name}(", ')') do
|
45
|
+
pp.nest 1 do
|
46
|
+
pp.breakable ''
|
47
|
+
pp.text 'authorization_header='
|
48
|
+
pp.pp @authorization_header
|
49
|
+
end
|
50
|
+
pp.breakable ''
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Guest = new(nil)
|
55
|
+
|
56
|
+
class << self
|
57
|
+
extend T::Sig
|
58
|
+
|
59
|
+
sig(:final) { params(key: String).returns(T.attached_class) }
|
60
|
+
def from_token(key)
|
61
|
+
new("Bearer #{key}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Client
|
6
|
+
# Etwin client interface
|
7
|
+
class EtwinClient
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
interface!
|
12
|
+
|
13
|
+
sig { abstract.params(auth: Auth, user_id: UserId).returns(User) }
|
14
|
+
def get_user(auth, user_id); end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Client
|
6
|
+
# HTTP Etwin client interface
|
7
|
+
class HttpEtwinClient
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
include EtwinClient
|
12
|
+
|
13
|
+
final!
|
14
|
+
|
15
|
+
sig(:final) { params(base_uri: URI::HTTP).void }
|
16
|
+
def initialize(base_uri)
|
17
|
+
@base_uri = T.let(base_uri.freeze, URI::HTTP)
|
18
|
+
freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
sig(:final) { params(auth: Auth, user_id: UserId).returns(User) }
|
22
|
+
def get_user(auth, user_id)
|
23
|
+
uri = resolve(['users', user_id.to_s])
|
24
|
+
res = Faraday.get uri
|
25
|
+
pp res
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
sig(:final) { params(segments: T::Array[String]).returns(URI::HTTP) }
|
31
|
+
def resolve(segments)
|
32
|
+
@base_uri.merge("api/v1/#{ segments * '/'}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/etwin/core.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Core
|
6
|
+
# Object type enum
|
7
|
+
class ObjectType < T::Enum
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
enums do
|
11
|
+
ClientForumActor = new('ClientForumActor')
|
12
|
+
ForumPost = new('ForumPost')
|
13
|
+
ForumPostRevision = new('ForumPostRevision')
|
14
|
+
ForumSection = new('ForumSection')
|
15
|
+
ForumThread = new('ForumThread')
|
16
|
+
HammerfestUser = new('HammerfestUser')
|
17
|
+
OauthClient = new('OauthClient')
|
18
|
+
RoleForumActor = new('RoleForumActor')
|
19
|
+
TwinoidUser = new('TwinoidUser')
|
20
|
+
User = new('User')
|
21
|
+
UserForumActor = new('UserForumActor')
|
22
|
+
end
|
23
|
+
|
24
|
+
sig { returns(String) }
|
25
|
+
def to_s
|
26
|
+
T.cast(serialize, String)
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { returns(String) }
|
30
|
+
def inspect
|
31
|
+
"ObjectType(#{self})"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
# Hammerfest module
|
6
|
+
module Hammerfest
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative './hammerfest/hammerfest_server'
|
11
|
+
require_relative './hammerfest/hammerfest_user_id'
|
12
|
+
require_relative './hammerfest/hammerfest_username'
|
13
|
+
require_relative './hammerfest/short_hammerfest_user'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Hammerfest
|
6
|
+
# A valid Hammerfest server
|
7
|
+
class HammerfestServer < T::Enum
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
enums do
|
11
|
+
HammerfestEs = new('hammerfest.es')
|
12
|
+
HammerfestFr = new('hammerfest.fr')
|
13
|
+
HfestNet = new('hfest.net')
|
14
|
+
end
|
15
|
+
|
16
|
+
sig { returns(String) }
|
17
|
+
def to_s
|
18
|
+
T.cast(serialize, String)
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { returns(String) }
|
22
|
+
def inspect
|
23
|
+
"HammerfestServer(#{self})"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Hammerfest
|
6
|
+
# A valid Hammerfest user id
|
7
|
+
class HammerfestUserId
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
final!
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
sig(:final) { returns(String) }
|
16
|
+
attr_reader :inner
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
sig(:final) { params(inner: String).void }
|
21
|
+
def initialize(inner)
|
22
|
+
@inner = T.let(inner.freeze, String)
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
sig(:final) { params(other: BasicObject).returns(T::Boolean) }
|
27
|
+
def ==(other)
|
28
|
+
case other
|
29
|
+
when HammerfestUserId
|
30
|
+
@inner == other.inner
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
sig(:final) { returns(Integer) }
|
37
|
+
def hash
|
38
|
+
@inner.hash
|
39
|
+
end
|
40
|
+
|
41
|
+
sig(:final) { returns(String) }
|
42
|
+
def to_s
|
43
|
+
@inner
|
44
|
+
end
|
45
|
+
|
46
|
+
sig(:final) { returns(String) }
|
47
|
+
def as_json
|
48
|
+
@inner
|
49
|
+
end
|
50
|
+
|
51
|
+
sig(:final) { returns(String) }
|
52
|
+
def inspect
|
53
|
+
"HammerfestUserId(#{@inner})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Hammerfest
|
6
|
+
# A valid Hammerfest username
|
7
|
+
class HammerfestUsername
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
final!
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
sig(:final) { returns(String) }
|
16
|
+
attr_reader :inner
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
sig(:final) { params(inner: String).void }
|
21
|
+
def initialize(inner)
|
22
|
+
@inner = T.let(inner.freeze, String)
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
sig(:final) { params(other: BasicObject).returns(T::Boolean) }
|
27
|
+
def ==(other)
|
28
|
+
case other
|
29
|
+
when HammerfestUsername
|
30
|
+
@inner == other.inner
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
sig(:final) { returns(Integer) }
|
37
|
+
def hash
|
38
|
+
@inner.hash
|
39
|
+
end
|
40
|
+
|
41
|
+
sig(:final) { returns(String) }
|
42
|
+
def to_s
|
43
|
+
@inner
|
44
|
+
end
|
45
|
+
|
46
|
+
sig(:final) { returns(String) }
|
47
|
+
def as_json
|
48
|
+
@inner
|
49
|
+
end
|
50
|
+
|
51
|
+
sig(:final) { returns(String) }
|
52
|
+
def inspect
|
53
|
+
"HammerfestUsername(#{@inner})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Etwin
|
5
|
+
module Hammerfest
|
6
|
+
# Hammerfest user reference with username
|
7
|
+
class ShortHammerfestUser
|
8
|
+
extend T::Helpers
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
final!
|
12
|
+
|
13
|
+
sig(:final) { returns(HammerfestServer) }
|
14
|
+
attr_reader :server
|
15
|
+
|
16
|
+
sig(:final) { returns(HammerfestUserId) }
|
17
|
+
attr_reader :id
|
18
|
+
|
19
|
+
sig(:final) { returns(HammerfestUsername) }
|
20
|
+
attr_reader :username
|
21
|
+
|
22
|
+
sig(:final) { params(server: HammerfestServer, id: HammerfestUserId, username: HammerfestUsername).void }
|
23
|
+
def initialize(server, id, username)
|
24
|
+
@server = T.let(server, HammerfestServer)
|
25
|
+
@id = T.let(id, HammerfestUserId)
|
26
|
+
@username = T.let(username, HammerfestUsername)
|
27
|
+
freeze
|
28
|
+
end
|
29
|
+
|
30
|
+
sig(:final) { params(other: BasicObject).returns(T::Boolean) }
|
31
|
+
def ==(other)
|
32
|
+
case other
|
33
|
+
when ShortHammerfestUser
|
34
|
+
@server == other.server && @id == other.id && @username == other.username
|
35
|
+
else
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
sig(:final) { returns(Integer) }
|
41
|
+
def hash
|
42
|
+
[@server, @id, @username].hash
|
43
|
+
end
|
44
|
+
|
45
|
+
# https://github.com/sorbet/sorbet/blob/master/rbi/stdlib/json.rbi#L194
|
46
|
+
sig(:final) { params(opts: T.untyped).returns(String) }
|
47
|
+
def to_json(opts = nil)
|
48
|
+
JSON.generate(as_json, opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
sig(:final) { returns(T::Hash[String, T.untyped]) }
|
52
|
+
def as_json
|
53
|
+
{
|
54
|
+
'server' => @server.serialize,
|
55
|
+
'id' => @id.as_json,
|
56
|
+
'username' => @username.as_json
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
sig(:final) { returns(String) }
|
61
|
+
def inspect
|
62
|
+
PP.singleline_pp(self, String.new)
|
63
|
+
end
|
64
|
+
|
65
|
+
sig(:final) { params(pp: T.untyped).returns(T.untyped) }
|
66
|
+
def pretty_print(pp) # rubocop:disable Metrics/MethodLength
|
67
|
+
pp.group(0, "#{self.class.name}(", ')') do
|
68
|
+
pp.nest 1 do
|
69
|
+
pp.breakable ''
|
70
|
+
pp.text 'server='
|
71
|
+
pp.pp @server
|
72
|
+
pp.text ','
|
73
|
+
pp.breakable ''
|
74
|
+
pp.text 'id='
|
75
|
+
pp.pp @id
|
76
|
+
pp.text ','
|
77
|
+
pp.breakable ''
|
78
|
+
pp.text 'username='
|
79
|
+
pp.pp @username
|
80
|
+
end
|
81
|
+
pp.breakable ''
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class << self
|
86
|
+
extend T::Sig
|
87
|
+
|
88
|
+
sig(:final) { params(json_str: String).returns(T.attached_class) }
|
89
|
+
def from_json(json_str)
|
90
|
+
deserialize JSON.parse(json_str)
|
91
|
+
end
|
92
|
+
|
93
|
+
sig(:final) { params(raw: T.untyped).returns(T.attached_class) }
|
94
|
+
def deserialize(raw)
|
95
|
+
server = HammerfestServer.deserialize(raw['server'])
|
96
|
+
id = HammerfestUserId.new(raw['id'])
|
97
|
+
username = HammerfestUsername.new(raw['username'])
|
98
|
+
new(server, id, username)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|