cuki 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/VERSION +1 -1
- data/cucumber.yml +2 -0
- data/cuki.gemspec +8 -2
- data/features/pull/pull.feature +3 -3
- data/features/pull/pull_single.feature +1 -1
- data/features/pull/splitting.feature +67 -0
- data/features/pull/tables.feature +2 -2
- data/features/pull/tags.feature +2 -2
- data/features/pull/textile.feature +2 -2
- data/features/push/push.feature +1 -1
- data/features/step_defs/pull_steps.rb +1 -1
- data/lib/cuki.rb +85 -17
- data/lib/string_utils.rb +9 -0
- metadata +29 -12
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|
data/cucumber.yml
ADDED
data/cuki.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "cuki"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Andy Waite"]
|
12
|
-
s.date = "2011-10-
|
12
|
+
s.date = "2011-10-17"
|
13
13
|
s.description = ""
|
14
14
|
s.email = "andy@andywaite.com"
|
15
15
|
s.executables = ["cuki"]
|
@@ -28,10 +28,12 @@ Gem::Specification.new do |s|
|
|
28
28
|
"Rakefile",
|
29
29
|
"VERSION",
|
30
30
|
"bin/cuki",
|
31
|
+
"cucumber.yml",
|
31
32
|
"cuki.gemspec",
|
32
33
|
"cuki.yaml.sample",
|
33
34
|
"features/pull/pull.feature",
|
34
35
|
"features/pull/pull_single.feature",
|
36
|
+
"features/pull/splitting.feature",
|
35
37
|
"features/pull/tables.feature",
|
36
38
|
"features/pull/tags.feature",
|
37
39
|
"features/pull/textile.feature",
|
@@ -43,6 +45,7 @@ Gem::Specification.new do |s|
|
|
43
45
|
"features/support/env.rb",
|
44
46
|
"lib/cuki.rb",
|
45
47
|
"lib/pusher.rb",
|
48
|
+
"lib/string_utils.rb",
|
46
49
|
"spec/cuki_spec.rb",
|
47
50
|
"spec/spec_helper.rb"
|
48
51
|
]
|
@@ -58,6 +61,7 @@ Gem::Specification.new do |s|
|
|
58
61
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
59
62
|
s.add_runtime_dependency(%q<httpclient>, [">= 0"])
|
60
63
|
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
64
|
+
s.add_runtime_dependency(%q<parallel>, [">= 0"])
|
61
65
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
62
66
|
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
63
67
|
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
@@ -66,6 +70,7 @@ Gem::Specification.new do |s|
|
|
66
70
|
else
|
67
71
|
s.add_dependency(%q<httpclient>, [">= 0"])
|
68
72
|
s.add_dependency(%q<nokogiri>, [">= 0"])
|
73
|
+
s.add_dependency(%q<parallel>, [">= 0"])
|
69
74
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
70
75
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
71
76
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
@@ -75,6 +80,7 @@ Gem::Specification.new do |s|
|
|
75
80
|
else
|
76
81
|
s.add_dependency(%q<httpclient>, [">= 0"])
|
77
82
|
s.add_dependency(%q<nokogiri>, [">= 0"])
|
83
|
+
s.add_dependency(%q<parallel>, [">= 0"])
|
78
84
|
s.add_dependency(%q<shoulda>, [">= 0"])
|
79
85
|
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
80
86
|
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
data/features/pull/pull.feature
CHANGED
@@ -38,12 +38,12 @@ Feature: Pull
|
|
38
38
|
This feature describes removing a product
|
39
39
|
</div>
|
40
40
|
"""
|
41
|
-
When I run `cuki pull`
|
41
|
+
When I run `cuki pull --skip-autoformat --skip-header`
|
42
42
|
Then the file "features/products/add_product.feature" should contain exactly:
|
43
43
|
"""
|
44
44
|
Feature: Add Product
|
45
45
|
|
46
|
-
http://example.com/pages/
|
46
|
+
http://example.com/pages/viewpage.action?pageId=123
|
47
47
|
|
48
48
|
|
49
49
|
This feature describes adding a product
|
@@ -53,7 +53,7 @@ Feature: Pull
|
|
53
53
|
"""
|
54
54
|
Feature: Remove Product
|
55
55
|
|
56
|
-
http://example.com/pages/
|
56
|
+
http://example.com/pages/viewpage.action?pageId=456
|
57
57
|
|
58
58
|
|
59
59
|
This feature describes removing a product
|
@@ -23,7 +23,7 @@ Feature: Pull single
|
|
23
23
|
This feature describes removing a product
|
24
24
|
</div>
|
25
25
|
"""
|
26
|
-
When I run `cuki pull features/products/add_product.feature`
|
26
|
+
When I run `cuki pull features/products/add_product.feature --skip-autoformat --skip-header`
|
27
27
|
Then a file named "features/products/add_product.feature" should exist
|
28
28
|
But the file "features/products/remove_product.feature" should not exist
|
29
29
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
Feature: Splitting
|
2
|
+
|
3
|
+
Instead of association one wiki page per feature file, you can split a wiki file across multiple feature files.
|
4
|
+
|
5
|
+
@announce
|
6
|
+
Scenario: Pull all features
|
7
|
+
Given a file named "config/cuki.yaml" with:
|
8
|
+
"""
|
9
|
+
---
|
10
|
+
host: http://example.com
|
11
|
+
container: !ruby/regexp '/h1\. Acceptance Criteria(.*)h1\./m'
|
12
|
+
mappings:
|
13
|
+
123: features/products
|
14
|
+
"""
|
15
|
+
And a Confluence page on "example.com" with id 123:
|
16
|
+
"""
|
17
|
+
<input id="content-title" value="Product Management">
|
18
|
+
<div id="markupTextarea">
|
19
|
+
h1. Acceptance Criteria
|
20
|
+
|
21
|
+
Something
|
22
|
+
|
23
|
+
h2. Add Product
|
24
|
+
|
25
|
+
This feature describes adding a product
|
26
|
+
|
27
|
+
h6. Scenario: Scenario A
|
28
|
+
|
29
|
+
h2. Remove Product
|
30
|
+
|
31
|
+
This feature describes removing a product
|
32
|
+
|
33
|
+
h6. Scenario Outline: Scenario B
|
34
|
+
|
35
|
+
h1. Next Section
|
36
|
+
</div>
|
37
|
+
"""
|
38
|
+
When I run `cuki pull --skip-autoformat --skip-header`
|
39
|
+
Then the file "features/products/add_product.feature" should contain exactly:
|
40
|
+
"""
|
41
|
+
Feature: Add Product
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
http://example.com/pages/viewpage.action?pageId=123#ProductManagement-AddProduct
|
46
|
+
|
47
|
+
This feature describes adding a product
|
48
|
+
|
49
|
+
Scenario: Scenario A
|
50
|
+
|
51
|
+
|
52
|
+
"""
|
53
|
+
And the file "features/products/remove_product.feature" should contain exactly:
|
54
|
+
"""
|
55
|
+
Feature: Remove Product
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
http://example.com/pages/viewpage.action?pageId=123#ProductManagement-RemoveProduct
|
60
|
+
|
61
|
+
This feature describes removing a product
|
62
|
+
|
63
|
+
Scenario Outline: Scenario B
|
64
|
+
|
65
|
+
|
66
|
+
"""
|
67
|
+
|
@@ -21,12 +21,12 @@ Feature: Tables
|
|
21
21
|
| b | 2 |
|
22
22
|
</div>
|
23
23
|
"""
|
24
|
-
When I run `cuki pull`
|
24
|
+
When I run `cuki pull --skip-autoformat --skip-header`
|
25
25
|
Then the file "features/products/add_product.feature" should contain exactly:
|
26
26
|
"""
|
27
27
|
Feature: Add Product
|
28
28
|
|
29
|
-
http://example.com/pages/
|
29
|
+
http://example.com/pages/viewpage.action?pageId=123
|
30
30
|
|
31
31
|
|
32
32
|
Scenario: Foo
|
data/features/pull/tags.feature
CHANGED
@@ -18,13 +18,13 @@ Feature: Tags
|
|
18
18
|
h5. Scenario: Foo
|
19
19
|
</div>
|
20
20
|
"""
|
21
|
-
When I run `cuki pull`
|
21
|
+
When I run `cuki pull --skip-autoformat --skip-header`
|
22
22
|
Then the file "features/products/add_product.feature" should contain exactly:
|
23
23
|
"""
|
24
24
|
@draft
|
25
25
|
Feature: Add Product
|
26
26
|
|
27
|
-
http://example.com/pages/
|
27
|
+
http://example.com/pages/viewpage.action?pageId=123
|
28
28
|
|
29
29
|
|
30
30
|
Scenario: Foo
|
@@ -17,12 +17,12 @@ Feature: Textile
|
|
17
17
|
h6. Scenario Outline: Bar
|
18
18
|
</div>
|
19
19
|
"""
|
20
|
-
When I run `cuki pull`
|
20
|
+
When I run `cuki pull --skip-autoformat --skip-header`
|
21
21
|
Then the file "features/products/add_product.feature" should contain exactly:
|
22
22
|
"""
|
23
23
|
Feature: Add Product
|
24
24
|
|
25
|
-
http://example.com/pages/
|
25
|
+
http://example.com/pages/viewpage.action?pageId=123
|
26
26
|
|
27
27
|
|
28
28
|
Scenario: Foo
|
data/features/push/push.feature
CHANGED
@@ -4,7 +4,7 @@ Given /^a Confluence page on "([^"]*)" with id (\d+):$/ do |host, id, content|
|
|
4
4
|
# the URLs to be stubbed
|
5
5
|
|
6
6
|
@stubs ||= {}
|
7
|
-
@stubs["http://#{host}/pages/
|
7
|
+
@stubs["http://#{host}/pages/viewpage.action?pageId=#{id}"] = content
|
8
8
|
|
9
9
|
File.open('stubs.json', 'w') do |f|
|
10
10
|
f.write @stubs.to_json
|
data/lib/cuki.rb
CHANGED
@@ -4,6 +4,8 @@ require 'nokogiri'
|
|
4
4
|
require 'yaml'
|
5
5
|
require 'CGI'
|
6
6
|
require 'json'
|
7
|
+
require 'parallel'
|
8
|
+
require File.dirname(__FILE__) + '/string_utils.rb'
|
7
9
|
|
8
10
|
# terrible hack
|
9
11
|
if File.exist?('stubs.json') || File.exist?('stubs.json')
|
@@ -25,18 +27,28 @@ class Cuki
|
|
25
27
|
configure_http_client
|
26
28
|
|
27
29
|
action = args.first
|
30
|
+
if args.include?('--skip-autoformat')
|
31
|
+
args.delete_if { |arg| '--skip-autoformat' == arg }
|
32
|
+
@skip_autoformat = true
|
33
|
+
end
|
34
|
+
if args.include?('--skip-header')
|
35
|
+
args.delete_if { |arg| '--skip-header' == arg }
|
36
|
+
@skip_header = true
|
37
|
+
end
|
28
38
|
if 'pull' == action
|
29
39
|
configure_pull_stubs
|
30
40
|
verify_project
|
31
41
|
file = args[1]
|
32
42
|
if file
|
33
43
|
id = @config['mappings'].invert[file]
|
34
|
-
raise "could not get id for #{file}"
|
44
|
+
raise "could not get id for #{file}" unless id
|
35
45
|
pull_feature id, file
|
36
46
|
else
|
37
|
-
@config['mappings']
|
47
|
+
Parallel.map(@config['mappings'], :in_processes => 4) do |id, filepath|
|
48
|
+
pull_feature id, filepath
|
49
|
+
end
|
38
50
|
end
|
39
|
-
|
51
|
+
autoformat
|
40
52
|
elsif 'push' == action
|
41
53
|
configure_push_stubs
|
42
54
|
Pusher.push file, @config
|
@@ -51,7 +63,7 @@ class Cuki
|
|
51
63
|
def verify_project
|
52
64
|
# check features folder exists
|
53
65
|
raise "features folder not found" unless File.exists?('features')
|
54
|
-
|
66
|
+
autoformat
|
55
67
|
end
|
56
68
|
|
57
69
|
def read_config
|
@@ -72,35 +84,80 @@ class Cuki
|
|
72
84
|
|
73
85
|
def pull_feature(id, filepath)
|
74
86
|
@content = ''
|
75
|
-
|
76
|
-
|
77
|
-
|
87
|
+
wiki_edit_link = @config['host'] + '/pages/editpage.action?pageId=' + id.to_s
|
88
|
+
wiki_view_link = @config['host'] + '/pages/viewpage.action?pageId=' + id.to_s
|
89
|
+
puts "Downloading #{wiki_edit_link}"
|
90
|
+
response = @client.get wiki_edit_link
|
78
91
|
doc = Nokogiri(response.body)
|
79
|
-
|
92
|
+
|
80
93
|
unless doc.at('#content-title')
|
81
94
|
puts "Not a valid confluence page:"
|
82
95
|
puts doc.to_s
|
83
96
|
exit(1)
|
84
97
|
end
|
85
98
|
|
86
|
-
@
|
87
|
-
|
88
|
-
|
99
|
+
if @config['container']
|
100
|
+
handle_multi doc, id
|
101
|
+
else
|
102
|
+
|
103
|
+
@content += "Feature: " + doc.at('#content-title')[:value] + "\n\n"
|
104
|
+
@content += "#{wiki_view_link}\n\n"
|
105
|
+
@content += get_markup(doc)
|
89
106
|
|
90
|
-
|
107
|
+
clean
|
91
108
|
|
92
|
-
|
109
|
+
process_tags
|
93
110
|
|
94
|
-
|
111
|
+
@content = "# Generated by Cuki (#{Time.now})\n" + @content unless @skip_header
|
112
|
+
|
113
|
+
save_file filepath
|
114
|
+
end
|
95
115
|
end
|
96
116
|
|
97
117
|
def autoformat
|
98
|
-
`cucumber -a . --dry-run -P` unless
|
118
|
+
`cucumber -a . --dry-run -P` unless @skip_autoformat
|
119
|
+
end
|
120
|
+
|
121
|
+
def handle_multi doc, id
|
122
|
+
feature_title_compressed = doc.at('#content-title')[:value].anchorize
|
123
|
+
|
124
|
+
@content += get_markup(doc)
|
125
|
+
acceptance_criteria_matches = @content.match(@config['container'])
|
126
|
+
unless acceptance_criteria_matches
|
127
|
+
puts "Could not match #{config['container']}"
|
128
|
+
exit(1)
|
129
|
+
end
|
130
|
+
acceptance_criteria = acceptance_criteria_matches[1]
|
131
|
+
scenario_titles = acceptance_criteria.scan(/h2. (.*)/).flatten
|
132
|
+
scenario_blocks = acceptance_criteria.split(/h2. .*/)
|
133
|
+
scenario_blocks.shift
|
134
|
+
|
135
|
+
combined = {}
|
136
|
+
scenario_titles.each_with_index do |title, index|
|
137
|
+
combined[title] = scenario_blocks[index].gsub(/h6. (.*)/, '\1')
|
138
|
+
end
|
139
|
+
combined.each do |title, content|
|
140
|
+
scenario_title_compressed = title.anchorize
|
141
|
+
feature_filename = title.parameterize
|
142
|
+
|
143
|
+
dirpath = @config['mappings'][id]
|
144
|
+
|
145
|
+
FileUtils.mkdir_p(dirpath)
|
146
|
+
|
147
|
+
fname = "#{dirpath}/#{feature_filename.gsub("\r", '')}.feature"
|
148
|
+
File.open(fname, 'w') do |f|
|
149
|
+
puts "Writing #{fname}"
|
150
|
+
f.write "Feature: #{title}\n\n"
|
151
|
+
link = @config['host'] + "/pages/viewpage.action?pageId=#{id}##{feature_title_compressed}-#{scenario_title_compressed}"
|
152
|
+
f.write "\n\n" + link
|
153
|
+
f.write content
|
154
|
+
end
|
155
|
+
end
|
99
156
|
end
|
100
157
|
|
101
158
|
def clean
|
102
159
|
|
103
|
-
@content.gsub(' ', '')
|
160
|
+
@content.gsub!(' ', '')
|
104
161
|
|
105
162
|
# remove the double pipes used for table headers in Confluence
|
106
163
|
@content.gsub!('||', '|')
|
@@ -113,6 +170,13 @@ class Cuki
|
|
113
170
|
# remove any unwanted headers
|
114
171
|
@content.gsub!(/h\d\. (Scenario: .*)/, '\1')
|
115
172
|
@content.gsub!(/h\d\. (Scenario Outline: .*)/, '\1')
|
173
|
+
@content.gsub!(/h\d\. (Background: .*)/, '\1')
|
174
|
+
|
175
|
+
#Remove fancy quotes
|
176
|
+
@content.gsub!('’', "'")
|
177
|
+
@content.gsub!('‘', "'")
|
178
|
+
@content.gsub!('“', '"')
|
179
|
+
@content.gsub!('”', '"')
|
116
180
|
|
117
181
|
end
|
118
182
|
|
@@ -134,7 +198,7 @@ class Cuki
|
|
134
198
|
def save_file(filepath)
|
135
199
|
dir_path = File.dirname(filepath)
|
136
200
|
|
137
|
-
|
201
|
+
FileUtils.mkdir_p(dir_path) unless File.exists?(dir_path)
|
138
202
|
|
139
203
|
File.open(filepath, 'w') do |f|
|
140
204
|
puts "Writing #{filepath}"
|
@@ -172,4 +236,8 @@ class Cuki
|
|
172
236
|
end
|
173
237
|
end
|
174
238
|
|
239
|
+
def get_markup(doc)
|
240
|
+
CGI.unescapeHTML(doc.css('#markupTextarea').text)
|
241
|
+
end
|
242
|
+
|
175
243
|
end
|
data/lib/string_utils.rb
ADDED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cuki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 7
|
10
|
+
version: 0.0.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Andy Waite
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-10-
|
18
|
+
date: 2011-10-17 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
requirement: &id001 !ruby/object:Gem::Requirement
|
@@ -56,11 +56,25 @@ dependencies:
|
|
56
56
|
- 0
|
57
57
|
version: "0"
|
58
58
|
version_requirements: *id003
|
59
|
+
name: parallel
|
60
|
+
prerelease: false
|
61
|
+
type: :runtime
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
hash: 3
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
version_requirements: *id004
|
59
73
|
name: shoulda
|
60
74
|
prerelease: false
|
61
75
|
type: :development
|
62
76
|
- !ruby/object:Gem::Dependency
|
63
|
-
requirement: &
|
77
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
64
78
|
none: false
|
65
79
|
requirements:
|
66
80
|
- - ~>
|
@@ -71,12 +85,12 @@ dependencies:
|
|
71
85
|
- 0
|
72
86
|
- 0
|
73
87
|
version: 1.0.0
|
74
|
-
version_requirements: *
|
88
|
+
version_requirements: *id005
|
75
89
|
name: bundler
|
76
90
|
prerelease: false
|
77
91
|
type: :development
|
78
92
|
- !ruby/object:Gem::Dependency
|
79
|
-
requirement: &
|
93
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
80
94
|
none: false
|
81
95
|
requirements:
|
82
96
|
- - ~>
|
@@ -87,12 +101,12 @@ dependencies:
|
|
87
101
|
- 6
|
88
102
|
- 4
|
89
103
|
version: 1.6.4
|
90
|
-
version_requirements: *
|
104
|
+
version_requirements: *id006
|
91
105
|
name: jeweler
|
92
106
|
prerelease: false
|
93
107
|
type: :development
|
94
108
|
- !ruby/object:Gem::Dependency
|
95
|
-
requirement: &
|
109
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
96
110
|
none: false
|
97
111
|
requirements:
|
98
112
|
- - ">="
|
@@ -101,12 +115,12 @@ dependencies:
|
|
101
115
|
segments:
|
102
116
|
- 0
|
103
117
|
version: "0"
|
104
|
-
version_requirements: *
|
118
|
+
version_requirements: *id007
|
105
119
|
name: rcov
|
106
120
|
prerelease: false
|
107
121
|
type: :development
|
108
122
|
- !ruby/object:Gem::Dependency
|
109
|
-
requirement: &
|
123
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
110
124
|
none: false
|
111
125
|
requirements:
|
112
126
|
- - ">="
|
@@ -115,7 +129,7 @@ dependencies:
|
|
115
129
|
segments:
|
116
130
|
- 0
|
117
131
|
version: "0"
|
118
|
-
version_requirements: *
|
132
|
+
version_requirements: *id008
|
119
133
|
name: rspec
|
120
134
|
prerelease: false
|
121
135
|
type: :development
|
@@ -139,10 +153,12 @@ files:
|
|
139
153
|
- Rakefile
|
140
154
|
- VERSION
|
141
155
|
- bin/cuki
|
156
|
+
- cucumber.yml
|
142
157
|
- cuki.gemspec
|
143
158
|
- cuki.yaml.sample
|
144
159
|
- features/pull/pull.feature
|
145
160
|
- features/pull/pull_single.feature
|
161
|
+
- features/pull/splitting.feature
|
146
162
|
- features/pull/tables.feature
|
147
163
|
- features/pull/tags.feature
|
148
164
|
- features/pull/textile.feature
|
@@ -154,6 +170,7 @@ files:
|
|
154
170
|
- features/support/env.rb
|
155
171
|
- lib/cuki.rb
|
156
172
|
- lib/pusher.rb
|
173
|
+
- lib/string_utils.rb
|
157
174
|
- spec/cuki_spec.rb
|
158
175
|
- spec/spec_helper.rb
|
159
176
|
homepage: http://github.com/andyw8/cuki
|