enterprise_mti 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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +32 -0
- data/lib/enterprise_mti.rb +9 -0
- data/lib/enterprise_mti/class_methods.rb +1 -0
- data/lib/enterprise_mti/class_methods/class_methods.rb +175 -0
- data/lib/enterprise_mti/migration.rb +1 -0
- data/lib/enterprise_mti/migration/migration.rb +34 -0
- data/lib/enterprise_mti/migration/sql_factory.rb +2 -0
- data/lib/enterprise_mti/migration/sql_factory/postgres_sql_factory.rb +16 -0
- data/lib/enterprise_mti/migration/sql_factory/sql_factory.rb +132 -0
- data/lib/enterprise_mti/version.rb +3 -0
- data/lib/tasks/enterprise_mti_tasks.rake +4 -0
- data/spec/class_methods_spec.rb +72 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/container.rb +3 -0
- data/spec/dummy/app/models/superthing.rb +3 -0
- data/spec/dummy/app/models/superthing_subclasses/subthing_one.rb +2 -0
- data/spec/dummy/app/models/superthing_subclasses/subthing_two.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +23 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/db/migrate/20131029213145_create_superthings.rb +9 -0
- data/spec/dummy/db/migrate/20131029213154_create_subthing_ones.rb +9 -0
- data/spec/dummy/db/migrate/20131029213203_create_subthing_twos.rb +9 -0
- data/spec/dummy/db/migrate/20131029213251_start_enterprise_mti.rb +9 -0
- data/spec/dummy/db/migrate/20131031131410_create_containers.rb +9 -0
- data/spec/dummy/db/schema.rb +42 -0
- data/spec/dummy/log/development.log +35 -0
- data/spec/dummy/log/test.log +1683 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/spec/models/container_spec.rb +5 -0
- data/spec/dummy/spec/models/subthing_one_spec.rb +5 -0
- data/spec/dummy/spec/models/subthing_two_spec.rb +5 -0
- data/spec/dummy/spec/models/superthing_spec.rb +5 -0
- data/spec/helpers/class_methods_spec_helpers.rb +5 -0
- data/spec/helpers/migration_spec_helpers.rb +42 -0
- data/spec/migration_spec.rb +74 -0
- data/spec/spec_helper.rb +24 -0
- metadata +220 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a2b1b8964e6ee113a1a9e254983691b9c26cbcdc
|
4
|
+
data.tar.gz: a14f2076d338fc4ee3833c179870e439ef623659
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b0d8aa4f7ca07d83f93e4ce7b2920a54d83a1569b7784947ab3820c58a532dba43f217195496bb1d8baf1bfe7765e7e1e354db37cc5e75c9d5562f7d4c874edc
|
7
|
+
data.tar.gz: d9c434e75b93e4fbef5f48956e981f18c7512c08af68317e7f112cc2ede9653962f349de4ef75cbcd719d74d4fc839671733a136b7e0b57ec171bacdd9c715de
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 Ersin Akinci
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'EnterpriseMti'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
Bundler::GemHelper.install_tasks
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
|
24
|
+
Rake::TestTask.new(:test) do |t|
|
25
|
+
t.libs << 'lib'
|
26
|
+
t.libs << 'test'
|
27
|
+
t.pattern = 'test/**/*_test.rb'
|
28
|
+
t.verbose = false
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
task default: :test
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'class_methods/class_methods'
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module EnterpriseMti
|
2
|
+
module ClassMethods
|
3
|
+
def has_subclass(subclass_symbol, options={})
|
4
|
+
|
5
|
+
subclass_name = subclass_symbol.to_s.camelize
|
6
|
+
subclass = nil
|
7
|
+
subclass_qualified_name = (options[:module] || '') + '::' + subclass_name
|
8
|
+
superclass = self
|
9
|
+
superclass_name = self.name.demodulize.underscore
|
10
|
+
superclass_symbol = self.name.demodulize.underscore.to_sym
|
11
|
+
table_name = options[:table_name] || subclass_symbol.to_s.pluralize
|
12
|
+
|
13
|
+
# Get subclass
|
14
|
+
Kernel.const_get(options[:module] || 'Kernel').module_eval {
|
15
|
+
subclass = const_get subclass_name }
|
16
|
+
|
17
|
+
subclass.class_eval do
|
18
|
+
# Set table name on subclass to prevent it from inheriting
|
19
|
+
# superclass' attributes, etc.
|
20
|
+
self.table_name = table_name
|
21
|
+
has_one superclass_symbol
|
22
|
+
attr_accessor :superclass_instance
|
23
|
+
validates :superclass_instance, presence: true
|
24
|
+
|
25
|
+
define_method "#{superclass_name}_transaction" do
|
26
|
+
superclass_instance.send "#{subclass_symbol.to_s}_id=", self.id
|
27
|
+
superclass_instance.save
|
28
|
+
end
|
29
|
+
after_save "#{superclass_name}_transaction".to_sym, on: :create
|
30
|
+
end
|
31
|
+
|
32
|
+
# Superclass belongs to subclass
|
33
|
+
self.belongs_to subclass_symbol, :class_name => subclass_qualified_name
|
34
|
+
end
|
35
|
+
|
36
|
+
def is_a_superclass
|
37
|
+
|
38
|
+
# Require subclasses' files as dependencies, otherwise we get circular
|
39
|
+
# dependency errors (see https://github.com/rails/rails/issues/3364)
|
40
|
+
if descendants.empty?
|
41
|
+
module_path_tokens = self.name.underscore.split('/')
|
42
|
+
module_path_tokens[-1] += '_subclasses'
|
43
|
+
Dir[Rails.root.join('app', 'models', File.join(module_path_tokens), "*.rb")].each do |file|
|
44
|
+
require_dependency file
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Call has_sublcass on the superclass for each subclass
|
49
|
+
descendants.each do |subclass|
|
50
|
+
name_tokens = subclass.to_s.split('::')
|
51
|
+
subclass_name = name_tokens.last
|
52
|
+
module_name = name_tokens[0...-1].join('::') if name_tokens.count > 1
|
53
|
+
self.send :has_subclass, subclass_name.underscore.to_sym, :module => module_name
|
54
|
+
end
|
55
|
+
|
56
|
+
# Populate container_classes, an array that contains classes that "have"
|
57
|
+
# the superclass (and thus have foreign key constraints on the
|
58
|
+
# superclass)
|
59
|
+
reflection_class = Proc.new do |r|
|
60
|
+
if r.options[:class_name]
|
61
|
+
r.options[:class_name].constantize
|
62
|
+
else
|
63
|
+
prefix = self.name.deconstantize || ''
|
64
|
+
"#{prefix}::#{r.name.to_s.camelize}".constantize
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
@container_classes = self.reflect_on_all_associations.keep_if { |r|
|
69
|
+
r.macro == :belongs_to &&
|
70
|
+
#self.column_names.include?(r.association_foreign_key) &&
|
71
|
+
!self.descendants.include?(reflection_class.call(r))
|
72
|
+
}.collect { |r| reflection_class.call(r) }
|
73
|
+
|
74
|
+
# Add read-only access to container_classes
|
75
|
+
class << self
|
76
|
+
def container_classes
|
77
|
+
@container_classes
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def has_one_superclass(superclass_symbol, options={})
|
83
|
+
|
84
|
+
superclass_name = superclass_symbol.to_s.camelize
|
85
|
+
superclass_qualified_name = (options[:module] || '') + '::' + superclass_name
|
86
|
+
superclass = superclass_qualified_name.constantize
|
87
|
+
container_class = self
|
88
|
+
container_class_camel_name = self.name.demodulize
|
89
|
+
container_class_underscore_name = self.name.demodulize.underscore
|
90
|
+
superclass_relation_name = superclass_symbol.to_s + "_superclass"
|
91
|
+
superclass_relation_symbol = superclass_relation_name.to_sym
|
92
|
+
|
93
|
+
has_one superclass_relation_symbol, class_name: superclass_qualified_name
|
94
|
+
|
95
|
+
# Create build and create methods for each subclass
|
96
|
+
superclass.descendants.each do |subclass|
|
97
|
+
subclass_underscore_name = subclass.name.demodulize.underscore
|
98
|
+
actions = [:build, :create, :create!]
|
99
|
+
|
100
|
+
actions.each do |action|
|
101
|
+
action_suffix = nil
|
102
|
+
action_name = action.to_s
|
103
|
+
|
104
|
+
if action.to_s.last(1) == '!'
|
105
|
+
action_suffix = '!'
|
106
|
+
action_name = action.to_s[0..-2]
|
107
|
+
end
|
108
|
+
|
109
|
+
subclass.define_singleton_method "#{action_name}_with_superclass_instance#{action_suffix}" do |*args, &block|
|
110
|
+
self.send("#{action_name}#{action_suffix}", *args, &block).superclass_instance = args.last[:superclass_instance]
|
111
|
+
end
|
112
|
+
|
113
|
+
superclass.class_eval do
|
114
|
+
|
115
|
+
define_method "#{action_name}_#{subclass_underscore_name}_subclass#{action_suffix}" do |*args, &block|
|
116
|
+
args.last[:superclass_instance] = self
|
117
|
+
subclass.send "#{action_name}_with_superclass_instance#{action_suffix}", *args, &block
|
118
|
+
end
|
119
|
+
|
120
|
+
define_method "#{subclass_underscore_name}_with_superclass_instance=" do |value|
|
121
|
+
value.superclass_instance = self
|
122
|
+
self.send "#{subclass_underscore_name}=", value
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
container_class.class_eval do
|
127
|
+
|
128
|
+
define_method "#{action_name}_#{subclass_underscore_name}#{action_suffix}" do |*args, &block|
|
129
|
+
unless superclass_instance = self.send(superclass_relation_symbol)
|
130
|
+
superclass_instance = self.send("build_#{superclass_relation_name}")
|
131
|
+
end
|
132
|
+
superclass_instance.instance_eval do
|
133
|
+
send "#{action_name}_#{subclass_underscore_name}_subclass#{action_suffix}", *args, &block
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Create getter
|
141
|
+
define_method superclass_symbol do
|
142
|
+
|
143
|
+
association_methods = superclass.descendants.collect { |subclass|
|
144
|
+
reflection_symbol =
|
145
|
+
subclass.to_s.demodulize.underscore.to_sym
|
146
|
+
assoc = superclass.reflect_on_association(reflection_symbol)
|
147
|
+
assoc ? assoc.name : nil
|
148
|
+
}.compact
|
149
|
+
|
150
|
+
if superclass_model_instance = self.send(superclass_relation_symbol)
|
151
|
+
association_methods.collect{ |a|
|
152
|
+
superclass_model_instance.send a
|
153
|
+
}.inject do |a, b|
|
154
|
+
a || b
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Create setter
|
160
|
+
define_method "#{superclass_symbol.to_s}=" do |value|
|
161
|
+
reflection_symbol =
|
162
|
+
value.class.name.demodulize.underscore.to_sym
|
163
|
+
|
164
|
+
unless superclass_instance = self.send(superclass_relation_symbol)
|
165
|
+
superclass_instance = self.send("build_#{superclass_relation_name}")
|
166
|
+
end
|
167
|
+
|
168
|
+
reflection_assignment_method =
|
169
|
+
superclass.reflect_on_association(reflection_symbol).name.to_s + '_with_superclass_instance='
|
170
|
+
|
171
|
+
superclass_instance.send reflection_assignment_method, value
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'migration/migration'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'sql_factory'
|
2
|
+
|
3
|
+
module EnterpriseMti
|
4
|
+
module Migration
|
5
|
+
|
6
|
+
def enterprise_mti_run(opts={})
|
7
|
+
if opts[:superclass_table] && opts[:subclass_tables] && opts[:direction]
|
8
|
+
|
9
|
+
case Rails.configuration.database_configuration[Rails.env]['adapter']
|
10
|
+
when 'postgresql'
|
11
|
+
sql_factory = SqlFactory::PostgresSqlFactory.new
|
12
|
+
end
|
13
|
+
|
14
|
+
sql_factory.superclass_table = opts[:superclass_table]
|
15
|
+
sql_factory.subclass_tables = opts[:subclass_tables]
|
16
|
+
|
17
|
+
sql = sql_factory.sql_for_up if opts[:direction] == :up
|
18
|
+
sql = sql_factory.sql_for_down if opts[:direction] == :down
|
19
|
+
|
20
|
+
execute sql
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def enterprise_mti_up(opts={})
|
25
|
+
enterprise_mti_run opts.merge!(direction: :up)
|
26
|
+
end
|
27
|
+
|
28
|
+
def enterprise_mti_down(opts={})
|
29
|
+
enterprise_mti_run opts.merge!(direction: :down)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
ActiveRecord::Migration.send :include, EnterpriseMti::Migration
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module EnterpriseMti
|
2
|
+
module Migration
|
3
|
+
module SqlFactory
|
4
|
+
|
5
|
+
class SqlFactory
|
6
|
+
attr_accessor :superclass_table, :subclass_tables
|
7
|
+
|
8
|
+
def sql_for_up
|
9
|
+
[superclass_table_up, subclass_tables_up].join
|
10
|
+
end
|
11
|
+
|
12
|
+
def sql_for_down
|
13
|
+
[subclass_tables_down, superclass_table_down].join
|
14
|
+
end
|
15
|
+
|
16
|
+
## Up methods (superclass) ##
|
17
|
+
|
18
|
+
def superclass_table_up
|
19
|
+
[superclass_table_foreign_keys_up, superclass_table_xor_constraint_up]
|
20
|
+
end
|
21
|
+
|
22
|
+
def superclass_table_foreign_keys_up
|
23
|
+
subclass_tables.map { |subclass_table|
|
24
|
+
alter_table superclass_table do
|
25
|
+
add_column "#{subclass_table}_id",
|
26
|
+
type: id_type,
|
27
|
+
nullable: false,
|
28
|
+
unique: true,
|
29
|
+
references: { table: subclass_table, column: "id" },
|
30
|
+
defer: true
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def superclass_table_xor_constraint_up
|
36
|
+
alter_table superclass_table do
|
37
|
+
add_constraint "#{superclass_table}_xor", type: :check do
|
38
|
+
subclass_tables.map { |subclass_table|
|
39
|
+
"(#{subclass_table}_id IS #{not_null})::#{integer}"
|
40
|
+
}.join(' + ') << ' = 1'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
## Up methods (subclass) ##
|
46
|
+
|
47
|
+
def subclass_tables_up
|
48
|
+
subclass_tables_id_alterations_up
|
49
|
+
end
|
50
|
+
|
51
|
+
def subclass_tables_id_alterations_up
|
52
|
+
subclass_tables.map { |subclass_table|
|
53
|
+
alter_table subclass_table do
|
54
|
+
add_constraint "id_fkey", type: :foreign_key, column: 'id' do
|
55
|
+
options_parser references: { table: superclass_table, column: "#{subclass_table}_id" }, defer: true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
## Down methods (superclass) ##
|
62
|
+
|
63
|
+
def superclass_table_down
|
64
|
+
[superclass_table_xor_constraint_down, superclass_table_foreign_keys_down]
|
65
|
+
end
|
66
|
+
|
67
|
+
def superclass_table_xor_constraint_down
|
68
|
+
alter_table superclass_table do
|
69
|
+
drop_constraint "#{superclass_table}_xor"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def superclass_table_foreign_keys_down
|
74
|
+
subclass_tables.map do |subclass_table|
|
75
|
+
alter_table superclass_table do
|
76
|
+
drop_column("#{subclass_table}_id")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
## Down methods (subclass) ##
|
82
|
+
|
83
|
+
def subclass_tables_down
|
84
|
+
subclass_tables_id_alterations_down
|
85
|
+
end
|
86
|
+
|
87
|
+
def subclass_tables_id_alterations_down
|
88
|
+
subclass_tables.map do |subclass_table|
|
89
|
+
alter_table subclass_table do
|
90
|
+
drop_constraint 'id_fkey'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
## DSL ##
|
96
|
+
|
97
|
+
def options_parser(opts={})
|
98
|
+
sql = []
|
99
|
+
sql.push opts[:type]
|
100
|
+
sql.push not_null if opts[:nullable] == false
|
101
|
+
sql.push unique if opts[:unique]
|
102
|
+
sql.push references table: opts[:references][:table], column: opts[:references][:column] if opts[:references]
|
103
|
+
sql.push defer if opts[:defer]
|
104
|
+
sql.join(' ')
|
105
|
+
end
|
106
|
+
def alter_table(table); "ALTER TABLE #{table} #{yield};"; end
|
107
|
+
def add_column(column, opts={})
|
108
|
+
"ADD COLUMN #{column} #{options_parser opts}"
|
109
|
+
end
|
110
|
+
def alter_column(column, opts={})
|
111
|
+
"ALTER COLUMN #{column} SET #{options_parser opts}"
|
112
|
+
end
|
113
|
+
def drop_column(column); "DROP COLUMN #{column}"; end
|
114
|
+
def not_null; "NOT NULL"; end
|
115
|
+
def unique; "UNIQUE"; end
|
116
|
+
def references(opts={}); "REFERENCES #{opts[:table]}(#{opts[:column]})"; end
|
117
|
+
def add_constraint(name, opts={})
|
118
|
+
case opts[:type]
|
119
|
+
when :check
|
120
|
+
type = 'CHECK ('
|
121
|
+
suffix = ')'
|
122
|
+
when :foreign_key
|
123
|
+
type = "FOREIGN KEY(#{opts[:column]})"
|
124
|
+
end
|
125
|
+
"ADD CONSTRAINT #{name} #{type} #{yield} #{suffix}"
|
126
|
+
end
|
127
|
+
def drop_constraint(name); "DROP CONSTRAINT #{name}"; end
|
128
|
+
def defer; "DEFERRABLE INITIALLY DEFERRED"; end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|