json-spec 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/.travis.yml +13 -0
- data/Gemfile +3 -0
- data/README.md +27 -26
- data/cachivache/.gitignore +1 -1
- data/cachivache/README.md +176 -96
- data/cachivache/Rakefile +18 -9
- data/cachivache/Vagrantfile +3 -3
- data/cachivache/cachivache.rb +4 -6
- data/cachivache/lib/rake-helper.rb +27 -105
- data/cachivache/lib/shell-contexts/shell-context.rb +63 -0
- data/cachivache/lib/shell-contexts/shell-exec.rb +19 -0
- data/cachivache/lib/shell-contexts/shell_buffer.rb +25 -0
- data/cachivache/lib/shell-contexts/shell_debugger.rb +11 -0
- data/cachivache/lib/{sh-file-context.rb → shell-file-context.rb} +13 -16
- data/cachivache/lib/shell-if-context.rb +21 -0
- data/cachivache/lib/stuff-api-behaviour.rb +145 -0
- data/cachivache/lib/stuff-configuration.rb +55 -0
- data/cachivache/lib/stuff-reminders-behaviour.rb +11 -0
- data/cachivache/stuff/ruby-json-spec.rb +10 -14
- data/json-spec.gemspec +4 -2
- data/lib/cabeza-de-termo/json-spec/expectations-library/default-expectations/default-expectations-mapping.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-builders/expectations-definition-builder.rb +2 -2
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/expectation-library-definition-builder.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expectations-library/definition-builders/modifier-builders/modifiers-definition-builder.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expectations-library/expectations-library.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expressions/json-any-of.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expressions/json-each-field.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expressions/json-each.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expressions/json-expression.rb +2 -2
- data/lib/cabeza-de-termo/json-spec/expressions/json-field.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expressions/json-list.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expressions/json-object.rb +1 -1
- data/lib/cabeza-de-termo/json-spec/expressions/json-spec.rb +4 -4
- data/lib/cabeza-de-termo/json-spec/version.rb +1 -1
- metadata +27 -21
- data/cachivache/lib/let-behaviour.rb +0 -27
- data/cachivache/lib/sh-if-context.rb +0 -31
- data/lib/cabeza-de-termo/json-spec/utilities/bind.rb +0 -20
data/cachivache/Rakefile
CHANGED
@@ -1,19 +1,28 @@
|
|
1
1
|
require_relative 'lib/rake-helper'
|
2
2
|
|
3
|
-
raise_validation_error "Please
|
3
|
+
raise_validation_error "Please define the parameter Cachivache.git_user_email in './cachivache.rb' run 'vagrant provision' again" unless Cachivache.is_defined?(:git_user_email)
|
4
|
+
raise_validation_error "Please define the parameter Cachivache.git_user_name in './cachivache.rb' run 'vagrant provision' again" unless Cachivache.is_defined?(:git_user_name)
|
5
|
+
raise_validation_error "Please define some Cachivache.stuff_to_install in './cachivache.rb' run 'vagrant provision' again" if Cachivache.stuff_to_install.empty?
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
def provision_stuff_to_install()
|
8
|
+
stuff_to_install do |stuff_name|
|
9
|
+
show_info "Provisioning #{stuff_name}"
|
10
|
+
invoke stuff_name
|
11
|
+
end
|
12
|
+
show_info "All done!!!"
|
13
|
+
end
|
8
14
|
|
9
|
-
|
10
|
-
|
15
|
+
stuff :provision do
|
16
|
+
provision_stuff_to_install
|
17
|
+
end
|
11
18
|
|
12
|
-
|
19
|
+
stuff :explain do
|
20
|
+
ShellContext.set_current ShellDebugger.new
|
21
|
+
provision_stuff_to_install
|
13
22
|
end
|
14
23
|
|
15
|
-
|
24
|
+
stuff default: :provision
|
16
25
|
|
17
26
|
at_exit do
|
18
|
-
|
27
|
+
show_reminders
|
19
28
|
end
|
data/cachivache/Vagrantfile
CHANGED
@@ -22,11 +22,11 @@ Vagrant.configure(2) do |config|
|
|
22
22
|
# backing providers for Vagrant. These expose provider-specific options.
|
23
23
|
|
24
24
|
config.vm.provider "virtualbox" do |vb|
|
25
|
-
|
25
|
+
# Display the VirtualBox GUI when booting the machine
|
26
26
|
vb.gui = Cachivache.wants_gui?
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
# Customize the amount of memory on the VM:
|
29
|
+
vb.memory = Cachivache.wants_gui? ? "4096" : "1024"
|
30
30
|
end
|
31
31
|
|
32
32
|
# Enable provisioning with a shell script. Additional provisioners such as
|
data/cachivache/cachivache.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
require_relative 'lib/
|
2
|
-
|
3
|
-
class Cachivache
|
4
|
-
include LetBehaviour
|
1
|
+
require_relative 'lib/stuff-configuration'
|
5
2
|
|
3
|
+
class Cachivache < Stuff::Configuration
|
6
4
|
# Vagrant params
|
7
5
|
|
8
6
|
let(:vagrant_ip_address) { '192.168.0.101' }
|
@@ -29,7 +27,7 @@ class Cachivache
|
|
29
27
|
|
30
28
|
let(:stuff_to_install) {
|
31
29
|
[
|
32
|
-
'
|
30
|
+
'ruby-json-spec'
|
33
31
|
]
|
34
32
|
}
|
35
33
|
|
@@ -56,4 +54,4 @@ host bitbucket.org
|
|
56
54
|
User git
|
57
55
|
}
|
58
56
|
}
|
59
|
-
end
|
57
|
+
end
|
@@ -2,130 +2,52 @@ require 'rake'
|
|
2
2
|
require 'colorize'
|
3
3
|
require 'pathname'
|
4
4
|
|
5
|
-
require_relative 'let-behaviour'
|
6
|
-
require_relative 'sh-file-context'
|
7
|
-
require_relative 'sh-if-context'
|
8
5
|
require_relative '../cachivache'
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
# Require all .rb files in a folder
|
13
|
-
def require_all_rb_files_in(folder)
|
7
|
+
# Helper - Iterate all .rb files in a folder
|
8
|
+
def all_rb_files_in(folder, &block)
|
14
9
|
Dir[Pathname.new(folder) + '**/*.rb'].each do |file|
|
15
|
-
|
10
|
+
block.call(file)
|
16
11
|
end
|
17
12
|
end
|
18
13
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def execute(task_name, *args)
|
24
|
-
Rake::Task[task_name].execute(*args)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Examples:
|
28
|
-
#
|
29
|
-
# sh_unless file_exists: '/bla.conf' do
|
30
|
-
# sh %Q{
|
31
|
-
# ls -la
|
32
|
-
# rm -rf bla
|
33
|
-
# }
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# sh_unless folder_exists: '/bla' do
|
37
|
-
# sh %Q{
|
38
|
-
# ls -la
|
39
|
-
# rm -rf bla
|
40
|
-
# }
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# sh_unless file: '/bla', contains: 'some regex' do
|
44
|
-
# sh %Q{
|
45
|
-
# ls -la
|
46
|
-
# rm -rf bla
|
47
|
-
# }
|
48
|
-
# end
|
49
|
-
#
|
50
|
-
# sh_unless command: 'java -version', contains: 'java 1.8' do
|
51
|
-
# sh %Q{
|
52
|
-
# ls -la
|
53
|
-
# rm -rf bla
|
54
|
-
# }
|
55
|
-
# end
|
56
|
-
#
|
57
|
-
def sh_unless(params, &block)
|
58
|
-
if params.key?(:file_exists)
|
59
|
-
sh unless_file_exists(params[:file_exists], &block)
|
60
|
-
return
|
61
|
-
end
|
62
|
-
if params.key?(:folder_exists)
|
63
|
-
sh unless_folder_exists(params[:folder_exists], &block)
|
64
|
-
return
|
65
|
-
end
|
66
|
-
if params.key?(:file) && params.key?(:contains)
|
67
|
-
sh unless_regex_in_file(params[:contains], params[:file], &block)
|
68
|
-
return
|
69
|
-
end
|
70
|
-
if params.key?(:command) && params.key?(:contains)
|
71
|
-
sh unless_regex_in_command(params[:contains], params[:command], &block)
|
72
|
-
return
|
14
|
+
def stuff_to_install(&block)
|
15
|
+
Cachivache.stuff_to_install.each do |stuff_name|
|
16
|
+
block.call stuff_name
|
73
17
|
end
|
74
|
-
raise 'Invalid unless option'
|
75
18
|
end
|
76
19
|
|
77
|
-
|
78
|
-
ShIfContext.with(guard: "[ ! -e #{filename} ]", &block)
|
79
|
-
end
|
20
|
+
public
|
80
21
|
|
81
|
-
|
82
|
-
|
83
|
-
|
22
|
+
######################################
|
23
|
+
# require all the files in /lib first
|
24
|
+
######################################
|
84
25
|
|
85
|
-
|
86
|
-
|
26
|
+
all_rb_files_in Cachivache.cachivache_folder + '/lib' do |file|
|
27
|
+
require file
|
87
28
|
end
|
88
29
|
|
89
|
-
|
90
|
-
|
91
|
-
|
30
|
+
######################################
|
31
|
+
# Stuff Api behaviour
|
32
|
+
######################################
|
92
33
|
|
93
|
-
|
94
|
-
|
95
|
-
# sh_in_file "/bla.conf" do
|
96
|
-
# replace pattern: "%user%", with: "admin"
|
97
|
-
#
|
98
|
-
# append "[cachivache]"
|
99
|
-
# append "path='/bla'"
|
100
|
-
# end
|
101
|
-
def sh_in_file(filename, &block)
|
102
|
-
sh ShFileContext.with(file: filename, &block)
|
34
|
+
def stuff(task_definition, *args, &block)
|
35
|
+
task(task_definition, *args, &block)
|
103
36
|
end
|
104
37
|
|
105
|
-
def
|
106
|
-
|
38
|
+
def execute_shell_command(command)
|
39
|
+
sh command
|
107
40
|
end
|
108
41
|
|
109
|
-
|
110
|
-
|
111
|
-
# remind_to 'This is shown when the provision ends'
|
112
|
-
def remind_to(text)
|
113
|
-
reminders << text
|
114
|
-
end
|
42
|
+
include Stuff::ApiBehaviour
|
43
|
+
include Stuff::RemindersBehaviour
|
115
44
|
|
116
|
-
|
117
|
-
puts '-----------------------------------------------'.red
|
118
|
-
puts text.green
|
119
|
-
puts '-----------------------------------------------'.red
|
120
|
-
end
|
45
|
+
ShellContext.set_current( ShellExec.new(self) )
|
121
46
|
|
122
|
-
|
123
|
-
|
124
|
-
|
47
|
+
######################################
|
48
|
+
# Load the rest of the files
|
49
|
+
######################################
|
125
50
|
|
126
|
-
|
127
|
-
|
128
|
-
exit
|
51
|
+
all_rb_files_in Cachivache.cachivache_folder do |file|
|
52
|
+
require file
|
129
53
|
end
|
130
|
-
|
131
|
-
require_all_rb_files_in Cachivache.cachivache_folder
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class ShellContext
|
2
|
+
@current = nil
|
3
|
+
@reminders = []
|
4
|
+
|
5
|
+
def self.current()
|
6
|
+
@current
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.set_current(shell_context)
|
10
|
+
@current = shell_context
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.during_shell_context(new_shell_context, &block)
|
14
|
+
current_shell_context = @current
|
15
|
+
|
16
|
+
@current = new_shell_context
|
17
|
+
|
18
|
+
block.call
|
19
|
+
|
20
|
+
@current = current_shell_context
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.reminders()
|
24
|
+
@reminders
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.show_info(text)
|
28
|
+
puts '-----------------------------------------------'.red
|
29
|
+
puts text.green
|
30
|
+
puts '-----------------------------------------------'.red
|
31
|
+
end
|
32
|
+
|
33
|
+
# Instance methods
|
34
|
+
|
35
|
+
def shell(command)
|
36
|
+
raise 'Subclass responsibility'
|
37
|
+
end
|
38
|
+
|
39
|
+
def shell!(command)
|
40
|
+
raise 'Subclass responsibility'
|
41
|
+
end
|
42
|
+
|
43
|
+
def invoke(task_name, *args)
|
44
|
+
Rake::Task[task_name].invoke(*args)
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute(task_name, *args)
|
48
|
+
Rake::Task[task_name].execute(*args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def remind_to(text)
|
52
|
+
ShellContext.reminders << text
|
53
|
+
end
|
54
|
+
|
55
|
+
def raise_validation_error(message)
|
56
|
+
show_info(message)
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
|
60
|
+
def show_info(text)
|
61
|
+
ShellContext.show_info(text)
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'shell-context'
|
2
|
+
|
3
|
+
class ShellExec < ShellContext
|
4
|
+
def initialize(scope)
|
5
|
+
@scope = scope
|
6
|
+
end
|
7
|
+
|
8
|
+
def scope()
|
9
|
+
@scope
|
10
|
+
end
|
11
|
+
|
12
|
+
def shell(command)
|
13
|
+
shell! command
|
14
|
+
end
|
15
|
+
|
16
|
+
def shell!(command)
|
17
|
+
scope.execute_shell_command(command)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'shell-context'
|
2
|
+
|
3
|
+
class ShellBuffer < ShellContext
|
4
|
+
def initialize(underlaying_shell_context)
|
5
|
+
@underlaying_shell_context = underlaying_shell_context
|
6
|
+
@contents = ''
|
7
|
+
end
|
8
|
+
|
9
|
+
def underlaying_shell_context()
|
10
|
+
@underlaying_shell_context
|
11
|
+
end
|
12
|
+
|
13
|
+
def contents
|
14
|
+
@contents
|
15
|
+
end
|
16
|
+
|
17
|
+
def shell(command)
|
18
|
+
contents << "\n" unless contents.empty?
|
19
|
+
contents << command
|
20
|
+
end
|
21
|
+
|
22
|
+
def shell!(command)
|
23
|
+
underlaying_shell_context.shell! command
|
24
|
+
end
|
25
|
+
end
|
@@ -1,39 +1,36 @@
|
|
1
|
-
|
1
|
+
require_relative 'stuff-api-behaviour'
|
2
|
+
|
3
|
+
class ShellFileContext
|
4
|
+
include Stuff::ApiBehaviour
|
5
|
+
|
2
6
|
def self.with(args, &block)
|
3
7
|
new(args).evaluate_with block
|
4
8
|
end
|
5
9
|
|
6
10
|
def initialize(args)
|
7
11
|
@filename = args[:file]
|
8
|
-
@text = ''
|
9
|
-
end
|
10
|
-
|
11
|
-
def text
|
12
|
-
@text
|
13
12
|
end
|
14
13
|
|
15
14
|
def evaluate_with(block)
|
16
15
|
instance_eval(&block)
|
17
16
|
end
|
18
17
|
|
19
|
-
def <<(string)
|
20
|
-
text << "\n" unless @text.empty?
|
21
|
-
text << string
|
22
|
-
end
|
23
|
-
|
24
18
|
# replace pattern: "user: ''", with: "user: 'admin'"
|
25
19
|
def replace(params)
|
26
20
|
pattern = params[:pattern]
|
27
21
|
replacement = params[:with]
|
28
|
-
|
22
|
+
%Q{
|
23
|
+
sudo sed -i 's|#{pattern}|#{replacement}|g' #{@filename}
|
24
|
+
}
|
29
25
|
end
|
30
26
|
|
31
27
|
# append '[mongo]'
|
28
|
+
# append :new_line
|
32
29
|
def append(text)
|
33
|
-
|
34
|
-
end
|
30
|
+
text = "\n" if text == :new_line
|
35
31
|
|
36
|
-
|
37
|
-
|
32
|
+
%Q{
|
33
|
+
echo "#{text}" | sudo tee -a #{@filename}
|
34
|
+
}
|
38
35
|
end
|
39
36
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'stuff-api-behaviour'
|
2
|
+
|
3
|
+
class ShellIfContext
|
4
|
+
include Stuff::ApiBehaviour
|
5
|
+
|
6
|
+
def self.with(args, &block)
|
7
|
+
new(args).evaluate_with block
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(args)
|
11
|
+
@guard = args[:guard]
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate_with(block)
|
15
|
+
shell "if #{@guard}; then"
|
16
|
+
|
17
|
+
instance_eval(&block)
|
18
|
+
|
19
|
+
shell "fi"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Stuff
|
2
|
+
module ApiBehaviour
|
3
|
+
def shell_context()
|
4
|
+
ShellContext.current
|
5
|
+
end
|
6
|
+
|
7
|
+
def invoke(task_name, *args)
|
8
|
+
shell_context.invoke(task_name, *args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute(task_name, *args)
|
12
|
+
shell_context.execute(task_name, *args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def shell(command)
|
16
|
+
shell_context.shell(command)
|
17
|
+
end
|
18
|
+
|
19
|
+
def shell!(command)
|
20
|
+
shell_context.shell!(command)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Examples:
|
24
|
+
#
|
25
|
+
# shell_unless file_exists: '/bla.conf' do
|
26
|
+
# shell %Q{
|
27
|
+
# ls -la
|
28
|
+
# rm -rf bla
|
29
|
+
# }
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# shell_unless folder_exists: '/bla' do
|
33
|
+
# shell %Q{
|
34
|
+
# ls -la
|
35
|
+
# rm -rf bla
|
36
|
+
# }
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# shell_unless file: '/bla', contains: 'some regex' do
|
40
|
+
# shell %Q{
|
41
|
+
# ls -la
|
42
|
+
# rm -rf bla
|
43
|
+
# }
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# shell_unless command: 'java -version', contains: 'java 1.8' do
|
47
|
+
# shell %Q{
|
48
|
+
# ls -la
|
49
|
+
# rm -rf bla
|
50
|
+
# }
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
def shell_unless(params, &block)
|
54
|
+
if params.key?(:file_exists)
|
55
|
+
shell unless_file_exists(params[:file_exists], &block)
|
56
|
+
return
|
57
|
+
end
|
58
|
+
if params.key?(:folder_exists)
|
59
|
+
shell unless_folder_exists(params[:folder_exists], &block)
|
60
|
+
return
|
61
|
+
end
|
62
|
+
if params.key?(:file) && params.key?(:contains)
|
63
|
+
shell unless_regex_in_file(params[:contains], params[:file], &block)
|
64
|
+
return
|
65
|
+
end
|
66
|
+
if params.key?(:command) && params.key?(:contains)
|
67
|
+
shell unless_regex_in_command(params[:contains], params[:command], &block)
|
68
|
+
return
|
69
|
+
end
|
70
|
+
raise 'Invalid unless option'
|
71
|
+
end
|
72
|
+
|
73
|
+
# Examples:
|
74
|
+
#
|
75
|
+
# in_file "/bla.conf" do
|
76
|
+
# shell replace pattern: "%user%", with: "admin"
|
77
|
+
#
|
78
|
+
# shell append "[cachivache]"
|
79
|
+
# shell append "path='/bla'"
|
80
|
+
# end
|
81
|
+
def in_file(filename, &block)
|
82
|
+
script = during_shell_buffer do
|
83
|
+
ShellFileContext.with(file: filename, &block)
|
84
|
+
end
|
85
|
+
|
86
|
+
shell script
|
87
|
+
end
|
88
|
+
|
89
|
+
# Examples:
|
90
|
+
#
|
91
|
+
# remind_to 'This is shown when the provision ends'
|
92
|
+
def remind_to(text)
|
93
|
+
shell_context.remind_to(text)
|
94
|
+
end
|
95
|
+
|
96
|
+
def raise_validation_error(message)
|
97
|
+
shell_context.raise_validation_error(message)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Example:
|
101
|
+
#
|
102
|
+
# configure :Something do
|
103
|
+
# let(:bla) { '123' }
|
104
|
+
# let(:ble) { 'abc' }
|
105
|
+
# end
|
106
|
+
def configure(class_name, &block)
|
107
|
+
Stuff::Configuration.ensure_subclass_exists(class_name)
|
108
|
+
Stuff::Configuration.class_named(class_name).instance_eval(&block)
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def during_shell_buffer(&block)
|
114
|
+
temporary_shell_buffer = ShellBuffer.new(shell_context)
|
115
|
+
|
116
|
+
ShellContext.during_shell_context(temporary_shell_buffer, &block)
|
117
|
+
|
118
|
+
temporary_shell_buffer.contents
|
119
|
+
end
|
120
|
+
|
121
|
+
def unless_file_exists(filename, &block)
|
122
|
+
during_shell_buffer do
|
123
|
+
ShellIfContext.with(guard: "[ ! -e #{filename} ]", &block)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def unless_folder_exists(folder, &block)
|
128
|
+
during_shell_buffer do
|
129
|
+
ShellIfContext.with(guard: "[ ! -d #{folder} ]", &block)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def unless_regex_in_file(regex, filename, &block)
|
134
|
+
during_shell_buffer do
|
135
|
+
ShellIfContext.with(guard: "! grep -q \"#{regex}\" \"#{filename}\"", &block)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def unless_regex_in_command(regex, command, &block)
|
140
|
+
during_shell_buffer do
|
141
|
+
ShellIfContext.with(guard: "! #{command} | grep -q \"#{regex}\"", &block)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|