black_and_white 0.1.6 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +7 -1
- data/README.md +24 -12
- data/black_and_white.gemspec +5 -2
- data/lib/black_and_white.rb +2 -5
- data/lib/black_and_white/active_record/test.rb +1 -11
- data/lib/black_and_white/broker.rb +20 -0
- data/lib/black_and_white/error.rb +3 -0
- data/lib/black_and_white/helpers/active_record/database.rb +56 -0
- data/lib/black_and_white/helpers/active_record/utils.rb +21 -0
- data/lib/black_and_white/helpers/methods.rb +16 -0
- data/lib/black_and_white/hooks.rb +12 -2
- data/lib/black_and_white/mongoid.rb +48 -1
- data/lib/black_and_white/mongoid/test.rb +16 -0
- data/lib/black_and_white/version.rb +1 -1
- metadata +27 -10
- data/lib/black_and_white/active_record/error.rb +0 -5
- data/lib/black_and_white/helpers/database.rb +0 -54
- data/lib/black_and_white/helpers/utils.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54360b95b384a8d993009e4b6e30906e0263ea67
|
4
|
+
data.tar.gz: ee52743e185f957e779f23205154c8b4ec1919c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eeb439a94afe944d665a1ae89827e9506bae9466cc45a067f240c20ab892a4ce715b46834dc2c64b54917ebe6d631985a63363d35d8576198880df46044750a7
|
7
|
+
data.tar.gz: d4657badc6ea8d135dc9289b5697e4fd05da91be974c91a7ef0625027c5f9e75d1b89a46ec6f6d0e87b58e26f8c9c8d1b0a0c0389ae86a6728b6bb818f677b2d
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
Add this line to your application's Gemfile:
|
11
11
|
|
12
12
|
```ruby
|
13
|
-
gem 'black_and_white', '~> 0.
|
13
|
+
gem 'black_and_white', '~> 0.2.0'
|
14
14
|
```
|
15
15
|
|
16
16
|
And then execute:
|
@@ -22,7 +22,8 @@ Or install it yourself as:
|
|
22
22
|
$ gem install black_and_white
|
23
23
|
|
24
24
|
## Usage
|
25
|
-
Black And White is meant to work with Rails for the moment.
|
25
|
+
Black And White is meant to work with Rails for the moment. It has
|
26
|
+
support for ActiveRecord and Mongoid. In order to
|
26
27
|
use it first run:
|
27
28
|
```ruby
|
28
29
|
rails g black_and_white:config
|
@@ -39,22 +40,32 @@ end
|
|
39
40
|
|
40
41
|
BlackAndWhite::Hooks.init
|
41
42
|
```
|
43
|
+
The `Hooks` class requires all the necessary files for the detected
|
44
|
+
orm. If you are using `ActiveRecord` you need to generate the
|
45
|
+
corresponding migrations with:
|
42
46
|
|
43
|
-
After this run:
|
44
47
|
```ruby
|
45
48
|
rails g black_and_white:migrations
|
46
49
|
```
|
47
|
-
this will create the necessary migrations in the `db/migrate` folder.
|
48
50
|
Review them and then feel free to migrate. Keep in mind that they give
|
49
|
-
you only some very basic columns. You can add as much as you want
|
51
|
+
you only some very basic columns. You can add as much as you want and
|
52
|
+
extend the logic
|
50
53
|
|
51
|
-
### For ActiveRecord
|
54
|
+
### For ActiveRecord
|
52
55
|
Include the black_and_white module for activerecord interactions. The base class may have multiple a/b tests:
|
53
56
|
```ruby
|
54
57
|
class User < ActiveRecord::Base
|
55
58
|
include BlackAndWhite::ActiveRecord
|
56
59
|
end
|
57
60
|
```
|
61
|
+
### For Mongoid
|
62
|
+
Include the black_and_white module for activerecord interactions. The base class may have multiple a/b tests:
|
63
|
+
```ruby
|
64
|
+
class User
|
65
|
+
include Mongoid::Document
|
66
|
+
include BlackAndWhite::Mongoid
|
67
|
+
end
|
68
|
+
```
|
58
69
|
|
59
70
|
To create a new A/B test you can run:
|
60
71
|
```ruby
|
@@ -86,17 +97,18 @@ user.ab_participate!('My Inactive test', join_inactive: true)
|
|
86
97
|
user.ab_participate!('My Inactive test', raise_on_missing: true)
|
87
98
|
=> AbTestError, "no A/B Test with name My Inactive test exists or it is not active"
|
88
99
|
```
|
100
|
+
A note for `Mongoid`: If you have `Mongoid.raise_not_found_error` this
|
101
|
+
will raise the generic mongoid error for not found documents.
|
89
102
|
|
90
103
|
If you added additional db colums or you want to add or extend more logic you can
|
91
104
|
use the `add` method which evaluates the given block in the scope of
|
92
105
|
the main `BlackAndWhite` module. That way you don't have to worry about
|
93
|
-
code location
|
106
|
+
code location.
|
94
107
|
```ruby
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
end
|
108
|
+
BlackAndWhite.add(self) do
|
109
|
+
def my_new_method; end
|
110
|
+
private
|
111
|
+
attr_reader :my_property
|
100
112
|
end
|
101
113
|
```
|
102
114
|
|
data/black_and_white.gemspec
CHANGED
@@ -22,6 +22,8 @@ Gem::Specification.new do |spec|
|
|
22
22
|
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
23
|
end
|
24
24
|
|
25
|
+
spec.required_ruby_version = '>= 2.2.2'
|
26
|
+
|
25
27
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
28
|
spec.bindir = "exe"
|
27
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -30,8 +32,9 @@ Gem::Specification.new do |spec|
|
|
30
32
|
spec.add_development_dependency "bundler", "~> 1.11"
|
31
33
|
spec.add_development_dependency "rake", "~> 10.0"
|
32
34
|
spec.add_development_dependency "rspec", "~> 3.0"
|
33
|
-
spec.add_development_dependency "activerecord", "
|
34
|
-
spec.add_development_dependency "activesupport", "
|
35
|
+
spec.add_development_dependency "activerecord", ">= 4.2"
|
36
|
+
spec.add_development_dependency "activesupport", ">= 4.2"
|
35
37
|
spec.add_development_dependency "pg"
|
38
|
+
spec.add_development_dependency "mongoid", ">= 5.1.4"
|
36
39
|
spec.add_development_dependency 'database_cleaner'
|
37
40
|
end
|
data/lib/black_and_white.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
require "black_and_white/version"
|
2
2
|
require "black_and_white/config"
|
3
|
-
require "black_and_white/
|
4
|
-
require "black_and_white/active_record"
|
5
|
-
require "black_and_white/active_record/error"
|
6
|
-
require "black_and_white/helpers/utils"
|
3
|
+
require "black_and_white/broker"
|
7
4
|
|
8
5
|
module BlackAndWhite
|
9
6
|
|
10
7
|
def self.create(args = {})
|
11
|
-
|
8
|
+
Broker.invoke(:create, args)
|
12
9
|
end
|
13
10
|
|
14
11
|
def self.add(klass, &block)
|
@@ -1,10 +1,9 @@
|
|
1
|
-
require 'active_record'
|
2
1
|
require 'active_support/inflector'
|
3
|
-
require 'black_and_white/helpers/database'
|
4
2
|
|
5
3
|
module BlackAndWhite
|
6
4
|
module ActiveRecord
|
7
5
|
class Test < ::ActiveRecord::Base
|
6
|
+
include Helpers::Methods
|
8
7
|
self.table_name = BlackAndWhite.config.bw_main_table.to_s.pluralize
|
9
8
|
|
10
9
|
validates :name, uniqueness: true
|
@@ -12,15 +11,6 @@ module BlackAndWhite
|
|
12
11
|
has_and_belongs_to_many BlackAndWhite.config.bw_class_table,
|
13
12
|
join_table: BlackAndWhite.config.bw_join_table
|
14
13
|
|
15
|
-
def activate!
|
16
|
-
self.active = true
|
17
|
-
save!
|
18
|
-
end
|
19
|
-
|
20
|
-
def deactivate!
|
21
|
-
self.active = false
|
22
|
-
save!
|
23
|
-
end
|
24
14
|
end
|
25
15
|
end
|
26
16
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module BlackAndWhite
|
2
|
+
class Broker
|
3
|
+
|
4
|
+
class << self
|
5
|
+
attr_accessor :orm
|
6
|
+
|
7
|
+
def invoke(method, *args)
|
8
|
+
if orm == :active_record
|
9
|
+
BlackAndWhite::ActiveRecord::Test.send(method, *args)
|
10
|
+
elsif orm == :mongoid
|
11
|
+
BlackAndWhite::Mongoid::Test.send(method, *args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def register(type)
|
16
|
+
self.orm = type
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module BlackAndWhite
|
2
|
+
module Helpers
|
3
|
+
module ActiveRecord
|
4
|
+
module Database
|
5
|
+
def bw_tests_table_data
|
6
|
+
<<RUBY
|
7
|
+
t.string :name, unique: true, null: false
|
8
|
+
t.boolean :active, default: false
|
9
|
+
t.timestamps null: false
|
10
|
+
RUBY
|
11
|
+
end
|
12
|
+
|
13
|
+
def bw_relations_table_data
|
14
|
+
<<RUBY
|
15
|
+
t.references :#{bw_tests_class_table_singularize}, index: true
|
16
|
+
t.references :#{bw_tests_table_name_singularize}, index: true
|
17
|
+
RUBY
|
18
|
+
end
|
19
|
+
|
20
|
+
def bw_relations_table_name
|
21
|
+
BlackAndWhite.config.bw_join_table
|
22
|
+
end
|
23
|
+
|
24
|
+
def bw_tests_table_name
|
25
|
+
BlackAndWhite.config.bw_main_table
|
26
|
+
end
|
27
|
+
|
28
|
+
def bw_tests_table_name_singularize
|
29
|
+
BlackAndWhite.config.bw_main_table.to_s.singularize
|
30
|
+
end
|
31
|
+
|
32
|
+
def bw_tests_class
|
33
|
+
BlackAndWhite.config.bw_class
|
34
|
+
end
|
35
|
+
|
36
|
+
def bw_tests_class_table
|
37
|
+
BlackAndWhite.config.bw_class_table
|
38
|
+
end
|
39
|
+
|
40
|
+
def bw_tests_class_table_singularize
|
41
|
+
BlackAndWhite.config.bw_class_table.to_s.singularize
|
42
|
+
end
|
43
|
+
|
44
|
+
def bw_tests_table_name_pluralize
|
45
|
+
BlackAndWhite.config.bw_main_table.to_s.pluralize
|
46
|
+
end
|
47
|
+
|
48
|
+
def migration_version
|
49
|
+
if defined?(Rails) && Rails.version.start_with?('5')
|
50
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module BlackAndWhite
|
2
|
+
module Helpers
|
3
|
+
module ActiveRecord
|
4
|
+
module Utils
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def active_record_4?
|
8
|
+
::ActiveRecord::VERSION::MAJOR == 4
|
9
|
+
end
|
10
|
+
|
11
|
+
def active_record_5?
|
12
|
+
::ActiveRecord::VERSION::MAJOR == 5
|
13
|
+
end
|
14
|
+
|
15
|
+
def active_record_3?
|
16
|
+
::ActiveRecord::VERSION::MAJOR == 3
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,9 +1,19 @@
|
|
1
1
|
module BlackAndWhite
|
2
2
|
class Hooks
|
3
3
|
def self.init
|
4
|
+
require 'black_and_white'
|
5
|
+
require 'black_and_white/error'
|
6
|
+
require 'black_and_white/helpers/methods'
|
4
7
|
if defined?(::ActiveRecord)
|
5
|
-
|
6
|
-
require
|
8
|
+
BlackAndWhite::Broker.register(:active_record)
|
9
|
+
require 'black_and_white/active_record'
|
10
|
+
require 'black_and_white/active_record/test'
|
11
|
+
require 'black_and_white/helpers/active_record/utils'
|
12
|
+
require 'black_and_white/helpers/active_record/database'
|
13
|
+
elsif defined?(::Mongoid)
|
14
|
+
BlackAndWhite::Broker.register(:mongoid)
|
15
|
+
require 'black_and_white/mongoid'
|
16
|
+
require 'black_and_white/mongoid/test'
|
7
17
|
end
|
8
18
|
end
|
9
19
|
end
|
@@ -1 +1,48 @@
|
|
1
|
-
|
1
|
+
module BlackAndWhite
|
2
|
+
module Mongoid
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
has_and_belongs_to_many :ab_tests, class_name: 'BlackAndWhite::Mongoid::Test'
|
7
|
+
|
8
|
+
attr_reader :ab_test, :options
|
9
|
+
|
10
|
+
def ab_participate!(test_name, **options, &block)
|
11
|
+
@options = options
|
12
|
+
if (@ab_test = fetch_ab_test(test_name)).present?
|
13
|
+
conditions = true
|
14
|
+
conditions = yield(self) if block_given?
|
15
|
+
update_participant if conditions
|
16
|
+
else
|
17
|
+
missing_ab_test(test_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def ab_participates?(test_name)
|
22
|
+
ab_tests.detect { |ab_test| ab_test.name == test_name }.present?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def update_participant
|
28
|
+
ab_tests << ab_test
|
29
|
+
end
|
30
|
+
|
31
|
+
def missing_ab_test(test_name)
|
32
|
+
if options[:raise_on_missing]
|
33
|
+
raise AbTestError, "no A/B Test with name #{test_name} exists or it is not active"
|
34
|
+
end
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch_ab_test(name)
|
39
|
+
query = { name: name }
|
40
|
+
if options[:join_inactive] == false
|
41
|
+
query.merge!(active: true)
|
42
|
+
end
|
43
|
+
|
44
|
+
BlackAndWhite::Mongoid::Test.find_by(query)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module BlackAndWhite
|
2
|
+
module Mongoid
|
3
|
+
class Test
|
4
|
+
include ::Mongoid::Document
|
5
|
+
include Helpers::Methods
|
6
|
+
|
7
|
+
has_and_belongs_to_many BlackAndWhite.config.bw_class_table
|
8
|
+
|
9
|
+
field :name, type: String
|
10
|
+
field :description, type: String
|
11
|
+
field :active, type: Boolean, default: false
|
12
|
+
|
13
|
+
validates :name, uniqueness: true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: black_and_white
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Slaveykov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,28 +56,28 @@ dependencies:
|
|
56
56
|
name: activerecord
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '4.2'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '4.2'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: activesupport
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '4.2'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '4.2'
|
83
83
|
- !ruby/object:Gem::Dependency
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: mongoid
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 5.1.4
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 5.1.4
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: database_cleaner
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,13 +144,16 @@ files:
|
|
130
144
|
- codecov.yml
|
131
145
|
- lib/black_and_white.rb
|
132
146
|
- lib/black_and_white/active_record.rb
|
133
|
-
- lib/black_and_white/active_record/error.rb
|
134
147
|
- lib/black_and_white/active_record/test.rb
|
148
|
+
- lib/black_and_white/broker.rb
|
135
149
|
- lib/black_and_white/config.rb
|
136
|
-
- lib/black_and_white/
|
137
|
-
- lib/black_and_white/helpers/
|
150
|
+
- lib/black_and_white/error.rb
|
151
|
+
- lib/black_and_white/helpers/active_record/database.rb
|
152
|
+
- lib/black_and_white/helpers/active_record/utils.rb
|
153
|
+
- lib/black_and_white/helpers/methods.rb
|
138
154
|
- lib/black_and_white/hooks.rb
|
139
155
|
- lib/black_and_white/mongoid.rb
|
156
|
+
- lib/black_and_white/mongoid/test.rb
|
140
157
|
- lib/black_and_white/version.rb
|
141
158
|
- lib/generators/black_and_white/config_generator.rb
|
142
159
|
- lib/generators/black_and_white/migrations_generator.rb
|
@@ -155,7 +172,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
155
172
|
requirements:
|
156
173
|
- - ">="
|
157
174
|
- !ruby/object:Gem::Version
|
158
|
-
version:
|
175
|
+
version: 2.2.2
|
159
176
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
177
|
requirements:
|
161
178
|
- - ">="
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module BlackAndWhite
|
2
|
-
module Helpers
|
3
|
-
module Database
|
4
|
-
def bw_tests_table_data
|
5
|
-
<<RUBY
|
6
|
-
t.string :name, unique: true, null: false
|
7
|
-
t.boolean :active, default: false
|
8
|
-
t.timestamps null: false
|
9
|
-
RUBY
|
10
|
-
end
|
11
|
-
|
12
|
-
def bw_relations_table_data
|
13
|
-
<<RUBY
|
14
|
-
t.references :#{bw_tests_class_table_singularize}, index: true
|
15
|
-
t.references :#{bw_tests_table_name_singularize}, index: true
|
16
|
-
RUBY
|
17
|
-
end
|
18
|
-
|
19
|
-
def bw_relations_table_name
|
20
|
-
BlackAndWhite.config.bw_join_table
|
21
|
-
end
|
22
|
-
|
23
|
-
def bw_tests_table_name
|
24
|
-
BlackAndWhite.config.bw_main_table
|
25
|
-
end
|
26
|
-
|
27
|
-
def bw_tests_table_name_singularize
|
28
|
-
BlackAndWhite.config.bw_main_table.to_s.singularize
|
29
|
-
end
|
30
|
-
|
31
|
-
def bw_tests_class
|
32
|
-
BlackAndWhite.config.bw_class
|
33
|
-
end
|
34
|
-
|
35
|
-
def bw_tests_class_table
|
36
|
-
BlackAndWhite.config.bw_class_table
|
37
|
-
end
|
38
|
-
|
39
|
-
def bw_tests_class_table_singularize
|
40
|
-
BlackAndWhite.config.bw_class_table.to_s.singularize
|
41
|
-
end
|
42
|
-
|
43
|
-
def bw_tests_table_name_pluralize
|
44
|
-
BlackAndWhite.config.bw_main_table.to_s.pluralize
|
45
|
-
end
|
46
|
-
|
47
|
-
def migration_version
|
48
|
-
if defined?(Rails) && Rails.version.start_with?('5')
|
49
|
-
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module BlackAndWhite
|
2
|
-
module Helpers
|
3
|
-
module Utils
|
4
|
-
extend self
|
5
|
-
|
6
|
-
def active_record_4?
|
7
|
-
::ActiveRecord::VERSION::MAJOR == 4
|
8
|
-
end
|
9
|
-
|
10
|
-
def active_record_5?
|
11
|
-
::ActiveRecord::VERSION::MAJOR == 5
|
12
|
-
end
|
13
|
-
|
14
|
-
def active_record_3?
|
15
|
-
::ActiveRecord::VERSION::MAJOR == 3
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|