fog-dtdream 0.0.1

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.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +4 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +41 -0
  7. data/Rakefile +1 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +7 -0
  10. data/fog-dtdream.gemspec +32 -0
  11. data/lib/fog/dtdream/core.rb +48 -0
  12. data/lib/fog/dtdream/models/storage/directories.rb +45 -0
  13. data/lib/fog/dtdream/models/storage/directory.rb +71 -0
  14. data/lib/fog/dtdream/models/storage/file.rb +174 -0
  15. data/lib/fog/dtdream/models/storage/files.rb +145 -0
  16. data/lib/fog/dtdream/requests/storage/copy_object.rb +32 -0
  17. data/lib/fog/dtdream/requests/storage/delete_bucket.rb +26 -0
  18. data/lib/fog/dtdream/requests/storage/delete_container.rb +32 -0
  19. data/lib/fog/dtdream/requests/storage/delete_object.rb +50 -0
  20. data/lib/fog/dtdream/requests/storage/get_bucket.rb +140 -0
  21. data/lib/fog/dtdream/requests/storage/get_container.rb +67 -0
  22. data/lib/fog/dtdream/requests/storage/get_containers.rb +72 -0
  23. data/lib/fog/dtdream/requests/storage/get_object.rb +42 -0
  24. data/lib/fog/dtdream/requests/storage/get_object_http_url.rb +39 -0
  25. data/lib/fog/dtdream/requests/storage/get_object_https_url.rb +97 -0
  26. data/lib/fog/dtdream/requests/storage/head_object.rb +31 -0
  27. data/lib/fog/dtdream/requests/storage/list_buckets.rb +46 -0
  28. data/lib/fog/dtdream/requests/storage/list_objects.rb +105 -0
  29. data/lib/fog/dtdream/requests/storage/put_bucket.rb +19 -0
  30. data/lib/fog/dtdream/requests/storage/put_container.rb +30 -0
  31. data/lib/fog/dtdream/requests/storage/put_object.rb +196 -0
  32. data/lib/fog/dtdream/storage.rb +206 -0
  33. data/lib/fog/dtdream/version.rb +5 -0
  34. data/lib/fog/dtdream.rb +9 -0
  35. metadata +108 -0
@@ -0,0 +1,26 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ # Delete an existing bucket
6
+ #
7
+ # ==== Parameters
8
+ # * container<~String> - Name of container to delete
9
+ # * object<~String> - Name of object to delete
10
+ #
11
+ def delete_bucket(bucket)
12
+ location = get_bucket_location(bucket)
13
+ endpoint = "http://"+location+".aliyuncs.com"
14
+ resource = bucket+'/'
15
+ request(
16
+ :expects => 204,
17
+ :method => 'DELETE',
18
+ :bucket => bucket,
19
+ :resource => resource,
20
+ :endpoint => endpoint
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ # Delete an existing container
6
+ #
7
+ # ==== Parameters
8
+ # * container<~String> - Name of container to delete
9
+ # * options
10
+ #
11
+ def delete_container(container, options={})
12
+
13
+ bucket = options[:bucket]
14
+ bucket ||= @aliyun_oss_bucket
15
+ location = get_bucket_location(bucket)
16
+ endpoint = "http://"+location+".aliyuncs.com"
17
+ object = container+'/'
18
+ resource = bucket+'/'+object
19
+
20
+ request(
21
+ :expects => 204,
22
+ :method => 'DELETE',
23
+ :path => object,
24
+ :bucket => bucket,
25
+ :resource => resource,
26
+ :endpoint => endpoint
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ # Delete an existing object
6
+ #
7
+ # ==== Parameters
8
+ # * container<~String> - Name of container to delete
9
+ # * object<~String> - Name of object to delete
10
+ #
11
+ def delete_object(object, options)
12
+ bucket = options[:bucket]
13
+ bucket ||= @aliyun_oss_bucket
14
+ location = get_bucket_location(bucket)
15
+ endpoint = "http://"+location+".aliyuncs.com"
16
+ resource = bucket+'/'+object
17
+ request(
18
+ :expects => 204,
19
+ :method => 'DELETE',
20
+ :path => object,
21
+ :bucket => bucket,
22
+ :resource => resource,
23
+ :endpoint => endpoint
24
+ )
25
+ end
26
+
27
+
28
+ def abort_multipart_upload(bucket, object, endpoint, uploadid)
29
+ if (nil == endpoint)
30
+ location = get_bucket_location(bucket)
31
+ endpoint = "http://"+location+".aliyuncs.com"
32
+ end
33
+ path = object+"?uploadId="+uploadid
34
+ resource = bucket+'/'+path
35
+
36
+ ret = request(
37
+ :expects => 204,
38
+ :method => 'DELETE',
39
+ :path => path,
40
+ :bucket => bucket,
41
+ :resource => resource,
42
+ :endpoint => endpoint
43
+ )
44
+
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,140 @@
1
+ require 'xmlsimple'
2
+
3
+ module Fog
4
+ module Storage
5
+ class Dtdream
6
+ class Real
7
+ def get_bucket(bucket)
8
+ location = get_bucket_location(bucket)
9
+ endpoint = "http://"+location+".aliyuncs.com"
10
+ resource = bucket+'/'
11
+ ret = request(
12
+ :expects => [200, 203],
13
+ :method => 'GET',
14
+ :bucket => bucket,
15
+ :resource => resource,
16
+ :endpoint => endpoint
17
+ )
18
+ xml = ret.data[:body]
19
+ result = XmlSimple.xml_in(xml)
20
+ end
21
+
22
+ def get_bucket_location(bucket)
23
+ attribute = '?location'
24
+ resource = bucket+'/'+attribute
25
+ ret = request(
26
+ :expects => [200, 203],
27
+ :method => 'GET',
28
+ :path => attribute,
29
+ :bucket => bucket,
30
+ :resource => resource
31
+ )
32
+ location = XmlSimple.xml_in(ret.data[:body])
33
+ end
34
+
35
+ def get_bucket_acl(bucket)
36
+ location = get_bucket_location(bucket)
37
+ endpoint = "http://"+location+".aliyuncs.com"
38
+ attribute = '?acl'
39
+ resource = bucket+'/'+attribute
40
+ ret = request(
41
+ :expects => [200, 203],
42
+ :method => 'GET',
43
+ :path => attribute,
44
+ :bucket => bucket,
45
+ :resource => resource,
46
+ :endpoint => endpoint
47
+ )
48
+ acl = XmlSimple.xml_in(ret.data[:body])["AccessControlList"][0]["Grant"][0]
49
+ end
50
+
51
+ #{"AllowedOrigin"=>["1"], "AllowedMethod"=>["DELETE"], "AllowedHeader"=>["1"], "ExposeHeader"=>["1"], "MaxAgeSeconds"=>["1"]}
52
+ def get_bucket_CORSRules(bucket)
53
+ location = get_bucket_location(bucket)
54
+ endpoint = "http://"+location+".aliyuncs.com"
55
+ attribute = '?cors'
56
+ resource = bucket+'/'+attribute
57
+ ret = request(
58
+ :expects => [200, 203],
59
+ :method => 'GET',
60
+ :path => attribute,
61
+ :bucket => bucket,
62
+ :resource => resource,
63
+ :endpoint => endpoint
64
+ )
65
+ cors = XmlSimple.xml_in(ret.data[:body])["CORSRule"][0]
66
+ end
67
+
68
+ #{"ID"=>["651a67ed-5b4f-4d9b-87ae-49dbaaa4b0ad"], "Prefix"=>["1"], "Status"=>["Enabled"], "Expiration"=>[{"Days"=>["30"]}]}
69
+ def get_bucket_lifecycle(bucket)
70
+ location = get_bucket_location(bucket)
71
+ endpoint = "http://"+location+".aliyuncs.com"
72
+ attribute = '?lifecycle'
73
+ resource = bucket+'/'+attribute
74
+ ret = request(
75
+ :expects => [200, 203],
76
+ :method => 'GET',
77
+ :path => attribute,
78
+ :bucket => bucket,
79
+ :resource => resource,
80
+ :endpoint => endpoint
81
+ )
82
+ lifecycle = XmlSimple.xml_in(ret.data[:body])["Rule"][0]
83
+ end
84
+
85
+ #返回log的prefix,如果没有设置log开关,返回nil
86
+ def get_bucket_logging(bucket)
87
+ location = get_bucket_location(bucket)
88
+ endpoint = "http://"+location+".aliyuncs.com"
89
+ attribute = '?logging'
90
+ resource = bucket+'/'+attribute
91
+ ret = request(
92
+ :expects => [200, 203],
93
+ :method => 'GET',
94
+ :path => attribute,
95
+ :bucket => bucket,
96
+ :resource => resource,
97
+ :endpoint => endpoint
98
+ )
99
+ logging = XmlSimple.xml_in(ret.data[:body])["LoggingEnabled"][0]["TargetPrefix"]
100
+ end
101
+
102
+ #{"AllowEmptyReferer"=>["true"], "RefererList"=>[{}]}
103
+ def get_bucket_referer(bucket)
104
+ location = get_bucket_location(bucket)
105
+ endpoint = "http://"+location+".aliyuncs.com"
106
+ attribute = '?referer'
107
+ resource = bucket+'/'+attribute
108
+ ret = request(
109
+ :expects => [200, 203],
110
+ :method => 'GET',
111
+ :path => attribute,
112
+ :bucket => bucket,
113
+ :resource => resource,
114
+ :endpoint => endpoint
115
+ )
116
+ referer = XmlSimple.xml_in(ret.data[:body])
117
+ end
118
+
119
+ #{"IndexDocument"=>[{"Suffix"=>["abc.html"]}], "ErrorDocument"=>[{"Key"=>["error.html"]}]}
120
+ def get_bucket_website(bucket)
121
+ location = get_bucket_location(bucket)
122
+ endpoint = "http://"+location+".aliyuncs.com"
123
+ attribute = '?website'
124
+ resource = bucket+'/'+attribute
125
+ ret = request(
126
+ :expects => [200, 203],
127
+ :method => 'GET',
128
+ :path => attribute,
129
+ :bucket => bucket,
130
+ :resource => resource,
131
+ :endpoint => endpoint
132
+ )
133
+ website = XmlSimple.xml_in(ret.data[:body])
134
+ end
135
+
136
+ end
137
+
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,67 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ def get_container(container, options = {})
6
+ options = options.reject {|key, value| value.nil?}
7
+
8
+ bucket = options[:bucket]
9
+ bucket ||= @aliyun_oss_bucket
10
+
11
+ marker = options[:marker]
12
+ maxKeys = options[:maxKeys]
13
+ delimiter = '/'
14
+
15
+ path = ""
16
+
17
+ if container == "" || container == "." || container == nil
18
+ prefix = nil
19
+ else
20
+ prefix = container+'/'
21
+ end
22
+
23
+ if prefix
24
+ path+="?prefix="+prefix
25
+ if marker
26
+ path+="&marker="+marker
27
+ end
28
+ if maxKeys
29
+ path+="&maxKeys="+maxKeys
30
+ end
31
+ if delimiter
32
+ path+="&delimiter="+delimiter
33
+ end
34
+ elsif marker
35
+ path+="?marker="+marker
36
+ if maxKeys
37
+ path+="&maxKeys="+maxKeys
38
+ end
39
+ if delimiter
40
+ path+="&delimiter="+delimiter
41
+ end
42
+ elsif maxKeys
43
+ path+="?maxKeys="+maxKeys
44
+ if delimiter
45
+ path+="&delimiter="+delimiter
46
+ end
47
+ elsif delimiter
48
+ path+="?delimiter="+delimiter
49
+ end
50
+
51
+ location = get_bucket_location(bucket)
52
+ endpoint = "http://"+location+".aliyuncs.com"
53
+ resource = bucket+'/'
54
+ ret = request(
55
+ :expects => [200, 203, 400],
56
+ :method => 'GET',
57
+ :path => path,
58
+ :resource => resource,
59
+ :bucket => bucket
60
+ )
61
+ xml = ret.data[:body]
62
+ result = XmlSimple.xml_in(xml)["CommonPrefixes"]
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,72 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ # List existing storage containers
6
+ #
7
+ # ==== Parameters
8
+ # * options<~Hash>:
9
+ # * 'limit'<~Integer> - Upper limit to number of results returned
10
+ # * 'marker'<~String> - Only return objects with name greater than this value
11
+ #
12
+ # ==== Returns
13
+ #
14
+ def get_containers(options = {})
15
+ options = options.reject {|key, value| value.nil?}
16
+
17
+ bucket = options[:bucket]
18
+ bucket ||= @aliyun_oss_bucket
19
+ prefix = options[:prefix]
20
+ marker = options[:marker]
21
+ maxKeys = options[:maxKeys]
22
+ delimiter = '/'
23
+
24
+ path = ""
25
+
26
+ if prefix
27
+ path+="?prefix="+prefix
28
+ if marker
29
+ path+="&marker="+marker
30
+ end
31
+ if maxKeys
32
+ path+="&maxKeys="+maxKeys
33
+ end
34
+ if delimiter
35
+ path+="&delimiter="+delimiter
36
+ end
37
+
38
+ elsif marker
39
+ path+="?marker="+marker
40
+ if maxKeys
41
+ path+="&maxKeys="+maxKeys
42
+ end
43
+ if delimiter
44
+ path+="&delimiter="+delimiter
45
+ end
46
+
47
+ elsif maxKeys
48
+ path+="?maxKeys="+maxKeys
49
+ if delimiter
50
+ path+="&delimiter="+delimiter
51
+ end
52
+ elsif delimiter
53
+ path+="?delimiter="+delimiter
54
+ end
55
+
56
+ location = get_bucket_location(bucket)
57
+ endpoint = "http://"+location+".aliyuncs.com"
58
+ resource = bucket+'/'
59
+ ret = request(
60
+ :expects => [200, 203, 400],
61
+ :method => 'GET',
62
+ :path => path,
63
+ :resource => resource,
64
+ :bucket => bucket
65
+ )
66
+ xml = ret.data[:body]
67
+ result = XmlSimple.xml_in(xml)["CommonPrefixes"]
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ # Get details for object
6
+ #
7
+ # ==== Parameters
8
+ # * container<~String> - Name of container to look in
9
+ # * object<~String> - Name of object to look for
10
+ #
11
+ def get_object(object, range = nil, options = {})
12
+ options = options.reject {|key, value| value.nil?}
13
+ bucket = options[:bucket]
14
+ bucket ||= @aliyun_oss_bucket
15
+ endpoint = options[:endpoint]
16
+ if (nil == endpoint)
17
+ location = get_bucket_location(bucket)
18
+ endpoint = "http://"+location+".aliyuncs.com"
19
+ end
20
+ resource = bucket+'/'+object
21
+ para = {
22
+ :expects => [200, 206],
23
+ :method => 'GET',
24
+ :path => object,
25
+ :bucket => bucket,
26
+ :resource => resource,
27
+ :endpoint => endpoint
28
+ }
29
+
30
+ if range
31
+ rangeStr = "bytes="+range
32
+ para[:headers] = {'Range' => rangeStr}
33
+ end
34
+
35
+ ret = request(para)
36
+ return ret.data
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ # Get an expiring object http url
6
+ #
7
+ # ==== Parameters
8
+ # * container<~String> - Name of container containing object
9
+ # * object<~String> - Name of object to get expiring url for
10
+ # * expires<~Time> - An expiry time for this url
11
+ #
12
+ # ==== Returns
13
+ # * response<~Excon::Response>:
14
+ # * body<~String> - url for object
15
+ def get_object_http_url_public(object, expires, options = {})
16
+ options = options.reject {|key, value| value.nil?}
17
+ bucket = options[:bucket]
18
+ bucket ||= @aliyun_oss_bucket
19
+ acl = get_bucket_acl(bucket)
20
+ location = get_bucket_location(bucket)
21
+
22
+ if "private" == acl
23
+ expires_time = (Time.now.to_i + expires).to_s
24
+ resource = bucket+'/'+object
25
+ signature = sign("GET", expires_time, nil, resource)
26
+ url = "http://"+bucket+"."+location+".aliyuncs.com/"+object+
27
+ "?OSSAccessKeyId="+@aliyun_accesskey_id+"&Expires="+expires_time+
28
+ "&Signature="+URI.encode(signature,'/[^!*\'()\;?:@#&%=+$,{}[]<>`" ')
29
+ elsif "public-read" == acl or "public-read-write" == acl
30
+ url = "http://"+bucket+"."+location+".aliyuncs.com/"+object
31
+ else
32
+ url = "acl is wrong with value:"+acl
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,97 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ # Get an expiring object https url from Cloud Files
6
+ #
7
+ # ==== Parameters
8
+ # * container<~String> - Name of container containing object
9
+ # * object<~String> - Name of object to get expiring url for
10
+ # * expires<~Time> - An expiry time for this url
11
+ #
12
+ # ==== Returns
13
+ # * response<~Excon::Response>:
14
+ # * body<~String> - url for object
15
+ def get_object_https_url_public(object, expires, options = {})
16
+ options = options.reject {|key, value| value.nil?}
17
+ bucket = options[:bucket]
18
+ bucket ||= @aliyun_oss_bucket
19
+ acl = get_bucket_acl(bucket)
20
+ location = get_bucket_location(bucket)
21
+
22
+ if "private" == acl
23
+ expires_time = (Time.now.to_i + expires).to_s
24
+ resource = bucket+'/'+object
25
+ signature = sign("GET", expires_time, nil, resource)
26
+ url = "https://"+bucket+"."+location+".aliyuncs.com/"+object+
27
+ "?OSSAccessKeyId="+@aliyun_accesskey_id+"&Expires="+expires_time+
28
+ "&Signature="+URI.encode(signature,'/[^!*\'()\;?:@#&%=+$,{}[]<>`" ')
29
+ elsif "public-read" == acl or "public-read-write" == acl
30
+ url = "https://"+bucket+"."+location+".aliyuncs.com/"+object
31
+ else
32
+ url = "acl is wrong with value:"+acl
33
+ end
34
+ end
35
+ # creates a temporary url
36
+ #
37
+ # ==== Parameters
38
+ # * container<~String> - Name of container containing object
39
+ # * object<~String> - Name of object to get expiring url for
40
+ # * expires<~Time> - An expiry time for this url
41
+ # * method<~String> - The method to use for accessing the object (GET, PUT, HEAD)
42
+ # * options<~Hash> - An optional options hash
43
+ # * 'scheme'<~String> - The scheme to use (http, https)
44
+ # * 'host'<~String> - The host to use
45
+ # * 'port'<~Integer> - The port to use
46
+ #
47
+ # ==== Returns
48
+ # * response<~Excon::Response>:
49
+ # * body<~String> - url for object
50
+ #
51
+ # ==== See Also
52
+ # http://docs.rackspace.com/files/api/v1/cf-devguide/content/Create_TempURL-d1a444.html
53
+ def create_temp_url(container, object, expires, method, options = {})
54
+ raise ArgumentError, "Insufficient parameters specified." unless (container && object && expires && method)
55
+ raise ArgumentError, "Storage must be instantiated with the :openstack_temp_url_key option" if @openstack_temp_url_key.nil?
56
+
57
+ scheme = options[:scheme] || @scheme
58
+ host = options[:host] || @host
59
+ port = options[:port] || @port
60
+
61
+ # POST not allowed
62
+ allowed_methods = %w{GET PUT HEAD}
63
+ unless allowed_methods.include?(method)
64
+ raise ArgumentError.new("Invalid method '#{method}' specified. Valid methods are: #{allowed_methods.join(', ')}")
65
+ end
66
+
67
+ expires = expires.to_i
68
+ object_path_escaped = "#{@path}/#{Fog::OpenStack.escape(container)}/#{Fog::OpenStack.escape(object,"/")}"
69
+ object_path_unescaped = "#{@path}/#{Fog::OpenStack.escape(container)}/#{object}"
70
+ string_to_sign = "#{method}\n#{expires}\n#{object_path_unescaped}"
71
+
72
+ hmac = Fog::HMAC.new('sha1', @openstack_temp_url_key)
73
+ sig = sig_to_hex(hmac.sign(string_to_sign))
74
+
75
+ temp_url_options = {
76
+ :scheme => scheme,
77
+ :host => host,
78
+ :port => port,
79
+ :path => object_path_escaped,
80
+ :query => "temp_url_sig=#{sig}&temp_url_expires=#{expires}"
81
+ }
82
+ URI::Generic.build(temp_url_options).to_s
83
+ end
84
+
85
+ private
86
+
87
+ def sig_to_hex(str)
88
+ str.unpack("C*").map { |c|
89
+ c.to_s(16)
90
+ }.map { |h|
91
+ h.size == 1 ? "0#{h}" : h
92
+ }.join
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,31 @@
1
+ module Fog
2
+ module Storage
3
+ class Dtdream
4
+ class Real
5
+ # Get headers for object
6
+ #
7
+ # ==== Parameters
8
+ # * container<~String> - Name of container to look in
9
+ # * object<~String> - Name of object to look for
10
+ #
11
+ def head_object(object, options={})
12
+ bucket = options[:bucket]
13
+ bucket ||= @aliyun_oss_bucket
14
+ location = get_bucket_location(bucket)
15
+ endpoint = "http://"+location+".aliyuncs.com"
16
+ resource = bucket+'/'+object
17
+ ret = request(
18
+ :expects => [200, 404],
19
+ :method => 'HEAD',
20
+ :path => object,
21
+ :bucket => bucket,
22
+ :resource => resource,
23
+ :endpoint => endpoint
24
+ )
25
+ return ret
26
+
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,46 @@
1
+ require 'xmlsimple'
2
+
3
+ module Fog
4
+ module Storage
5
+ class Dtdream
6
+ class Real
7
+ def list_buckets(options={})
8
+ prefix = options[:prefix]
9
+ marker = options[:marker]
10
+ maxKeys = options[:maxKeys]
11
+
12
+ path = ""
13
+
14
+ if prefix
15
+ path+="?prefix="+prefix
16
+ if marker
17
+ path+="&marker="+marker
18
+ end
19
+ if maxKeys
20
+ path+="&maxKeys="+maxKeys
21
+ end
22
+
23
+ elsif marker
24
+ path+="?marker="+marker
25
+ if maxKeys
26
+ path+="&maxKeys="+maxKeys
27
+ end
28
+
29
+ elsif maxKeys
30
+ path+="?maxKeys="+maxKeys
31
+ end
32
+
33
+ ret = request(
34
+ :expects => [200, 203],
35
+ :method => 'GET',
36
+ :path => path
37
+ )
38
+ xml = ret.data[:body]
39
+ result = XmlSimple.xml_in(xml)["Buckets"][0]["Bucket"]
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
46
+ end