avm-tools 0.56.0 → 0.60.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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/lib/avm/local_projects.rb +9 -0
  3. data/lib/avm/local_projects/instance.rb +35 -0
  4. data/lib/avm/local_projects/jobs/update.rb +29 -0
  5. data/lib/avm/projects.rb +9 -0
  6. data/lib/avm/projects/stereotype.rb +62 -0
  7. data/lib/avm/projects/stereotypes.rb +20 -0
  8. data/lib/avm/projects/stereotypes/git.rb +25 -0
  9. data/lib/avm/projects/stereotypes/git/local_project_mixin.rb +19 -0
  10. data/lib/avm/projects/stereotypes/git/publish.rb +15 -0
  11. data/lib/avm/projects/stereotypes/git/warp.rb +27 -0
  12. data/lib/avm/projects/stereotypes/git_subrepo.rb +34 -0
  13. data/lib/avm/projects/stereotypes/git_subrepo/publish.rb +14 -0
  14. data/lib/avm/projects/stereotypes/git_subrepo/warp.rb +87 -0
  15. data/lib/avm/projects/stereotypes/git_subtree.rb +51 -0
  16. data/lib/avm/projects/stereotypes/git_subtree/publish.rb +12 -0
  17. data/lib/avm/projects/stereotypes/git_subtree/warp.rb +29 -0
  18. data/lib/avm/projects/stereotypes/rails_application.rb +23 -0
  19. data/lib/avm/projects/stereotypes/redmine_plugin.rb +23 -0
  20. data/lib/avm/projects/stereotypes/ruby_gem.rb +30 -0
  21. data/lib/avm/projects/stereotypes/ruby_gem/local_project_mixin.rb +19 -0
  22. data/lib/avm/projects/stereotypes/ruby_gem/publish.rb +129 -0
  23. data/lib/avm/projects/stereotypes/ruby_gem/update.rb +59 -0
  24. data/lib/avm/tools/runner/eac_redmine_base0/project_rename.rb +64 -0
  25. data/lib/avm/tools/runner/launcher.rb +10 -2
  26. data/lib/avm/tools/runner/launcher/instances.rb +45 -0
  27. data/lib/avm/tools/runner/launcher/projects.rb +50 -0
  28. data/lib/avm/tools/runner/launcher/publish.rb +65 -0
  29. data/lib/avm/tools/runner/local_project.rb +32 -0
  30. data/lib/avm/tools/runner/local_project/info.rb +25 -0
  31. data/lib/avm/tools/runner/local_project/update.rb +26 -0
  32. data/lib/avm/tools/version.rb +1 -1
  33. data/lib/eac_launcher.rb +1 -2
  34. data/lib/eac_launcher/context/instance_manager.rb +1 -0
  35. data/lib/eac_launcher/git/base/dirty_files.rb +5 -14
  36. data/lib/eac_launcher/git/publish_base.rb +1 -0
  37. data/lib/eac_launcher/git/sub_warp_base.rb +1 -1
  38. data/lib/eac_launcher/git/warp_base.rb +2 -2
  39. data/lib/eac_launcher/instances/base.rb +2 -2
  40. data/lib/eac_launcher/instances/runner_helper.rb +1 -1
  41. data/lib/eac_launcher/paths/logical.rb +2 -2
  42. data/vendor/eac_docker/Gemfile +5 -0
  43. data/vendor/eac_docker/eac_docker.gemspec +18 -0
  44. data/vendor/eac_docker/lib/eac_docker.rb +7 -0
  45. data/vendor/eac_docker/lib/eac_docker/container.rb +74 -0
  46. data/vendor/eac_docker/lib/eac_docker/executables.rb +18 -0
  47. data/vendor/eac_docker/lib/eac_docker/images.rb +9 -0
  48. data/vendor/eac_docker/lib/eac_docker/images/base.rb +14 -0
  49. data/vendor/eac_docker/lib/eac_docker/images/coded.rb +39 -0
  50. data/vendor/eac_docker/lib/eac_docker/images/named.rb +30 -0
  51. data/vendor/eac_docker/lib/eac_docker/images/templatized.rb +26 -0
  52. data/vendor/eac_docker/lib/eac_docker/registry.rb +17 -0
  53. data/vendor/eac_docker/lib/eac_docker/rspec.rb +17 -0
  54. data/vendor/eac_docker/lib/eac_docker/version.rb +5 -0
  55. data/vendor/eac_docker/spec/lib/eac_docker/executables_spec.rb +9 -0
  56. data/vendor/eac_docker/spec/lib/eac_docker/images/coded_spec.rb +12 -0
  57. data/vendor/eac_docker/spec/lib/eac_docker/images/coded_spec_files/image1/Dockerfile +1 -0
  58. data/vendor/eac_docker/spec/lib/eac_docker/images/templatized_spec.rb +17 -0
  59. data/vendor/eac_docker/spec/lib/eac_docker/images/templatized_spec_files/stub_docker_image/Dockerfile +1 -0
  60. data/vendor/eac_docker/spec/rubocop_spec.rb +7 -0
  61. data/vendor/eac_docker/spec/spec_helper.rb +103 -0
  62. data/vendor/eac_git/lib/eac_git/local.rb +1 -1
  63. data/vendor/eac_git/lib/eac_git/local/dirty_files.rb +38 -0
  64. data/vendor/eac_git/lib/eac_git/version.rb +1 -1
  65. data/vendor/eac_git/vendor/git-subrepo/lib/git-subrepo.d/bash+.bash +1 -1
  66. data/vendor/eac_ruby_gems_utils/LICENCE +16 -674
  67. data/vendor/eac_ruby_gems_utils/eac_ruby_gems_utils.gemspec +10 -2
  68. data/vendor/eac_ruby_gems_utils/lib/eac_ruby_gems_utils/gem.rb +2 -1
  69. data/vendor/eac_ruby_gems_utils/lib/eac_ruby_gems_utils/version.rb +1 -1
  70. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/boolean.rb +31 -0
  71. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/console/docopt_runner/_class_methods.rb +1 -1
  72. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/envs/ssh_env.rb +12 -9
  73. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/envs/ssh_env/dasho_options.rb +53 -0
  74. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/envs/ssh_env/identity_file.rb +25 -0
  75. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/envs/ssh_env/quiet.rb +24 -0
  76. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/envs/ssh_env/terminal.rb +34 -0
  77. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/fs_cache.rb +1 -0
  78. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/immutable.rb +15 -0
  79. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/immutable/array_accessor.rb +32 -0
  80. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/immutable/base_accessor.rb +23 -0
  81. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/immutable/boolean_accessor.rb +16 -0
  82. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/immutable/class_methods.rb +20 -0
  83. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/immutable/common_accessor.rb +30 -0
  84. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/immutable/hash_accessor.rb +46 -0
  85. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/immutable/instance_methods.rb +21 -0
  86. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/listable/list.rb +5 -0
  87. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/patches/module/immutable.rb +10 -0
  88. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/require_sub.rb +8 -8
  89. data/vendor/eac_ruby_utils/lib/eac_ruby_utils/version.rb +1 -1
  90. metadata +91 -27
  91. data/lib/eac_launcher/runner.rb +0 -21
  92. data/lib/eac_launcher/runner/instances.rb +0 -41
  93. data/lib/eac_launcher/runner/projects.rb +0 -46
  94. data/lib/eac_launcher/runner/publish.rb +0 -59
  95. data/lib/eac_launcher/stereotype.rb +0 -52
  96. data/lib/eac_launcher/stereotypes.rb +0 -14
  97. data/lib/eac_launcher/stereotypes/git.rb +0 -21
  98. data/lib/eac_launcher/stereotypes/git/publish.rb +0 -13
  99. data/lib/eac_launcher/stereotypes/git/warp.rb +0 -25
  100. data/lib/eac_launcher/stereotypes/git_subrepo.rb +0 -30
  101. data/lib/eac_launcher/stereotypes/git_subrepo/publish.rb +0 -12
  102. data/lib/eac_launcher/stereotypes/git_subrepo/warp.rb +0 -85
  103. data/lib/eac_launcher/stereotypes/git_subtree.rb +0 -47
  104. data/lib/eac_launcher/stereotypes/git_subtree/publish.rb +0 -10
  105. data/lib/eac_launcher/stereotypes/git_subtree/warp.rb +0 -27
  106. data/lib/eac_launcher/stereotypes/rails.rb +0 -21
  107. data/lib/eac_launcher/stereotypes/redmine_plugin.rb +0 -21
  108. data/lib/eac_launcher/stereotypes/ruby_gem.rb +0 -27
  109. data/lib/eac_launcher/stereotypes/ruby_gem/publish.rb +0 -127
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '085a810ef05afe2de594a7acfc667d6634c431392f2e1d290ee5b9b65151bc56'
4
- data.tar.gz: 0c3a8e05eebbaeb7a6d0dbe2fb97a8864628746e694b12922b5a1db032354577
3
+ metadata.gz: 3a03f7f27fdfdd821203ba28f951ca16404a91b886453f9839e855dcf78627ef
4
+ data.tar.gz: 8f02cc5b2e5454e4e5253be317e73c94d8f007538b3464281f9a84c1bd83768f
5
5
  SHA512:
6
- metadata.gz: 12cc99637f0413895661d8598b383e446cf9750665ec9ba256152595bf9a2c188b64d420965e0e95b4d887ac50482754256379b169a6f743b393a1a343682a3d
7
- data.tar.gz: 4c477631dcfad184ad24b0458b4da6e41b4a7c2f9a1672aaeea0c36c6145daebf27c7c67720b79e69d9da8def06e5d6deb11575a5b5f6a0c69a95a88b922ee56
6
+ metadata.gz: dd7d158d5b73aea3e54c715b2a13ec15fa3bc5fd6f87b3252f70ae9b0fd8dce36fb3a6b9c1d043f0b2365087848627595aaf7041142bf27f18d60a3f1ac4e0a9
7
+ data.tar.gz: a8300831601639f79484dcd81934900c4be5003e85ed4e5a6345bb0c19ea5f4b3149556633238bd63fff97c933df7535622bfe588021c06e0567a064ccaca7e2
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module LocalProjects
7
+ require_sub __FILE__
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_launcher/paths/real'
4
+ require 'eac_ruby_utils/core_ext'
5
+ require 'avm/projects/stereotypes'
6
+
7
+ module Avm
8
+ module LocalProjects
9
+ class Instance
10
+ enable_simple_cache
11
+ common_constructor :path do
12
+ self.path = path.to_pathname
13
+ source_stereotypes_mixins
14
+ end
15
+
16
+ # Backward compatibility with [EacLauncher::Paths::Logical].
17
+ # @return [EacLauncher::Paths::Real].
18
+ def real
19
+ ::EacLauncher::Paths::Real.new(path.to_path)
20
+ end
21
+
22
+ private
23
+
24
+ def stereotypes_uncached
25
+ ::Avm::Projects::Stereotypes.list.select { |s| s.match?(self) }
26
+ end
27
+
28
+ def source_stereotypes_mixins
29
+ stereotypes.each do |s|
30
+ s.local_project_mixin_module.if_present { |v| extend(v) }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module LocalProjects
7
+ module Jobs
8
+ class Update
9
+ enable_console_speaker
10
+ common_constructor :instance
11
+
12
+ def run
13
+ instance.stereotypes.each { |stereotype| run_stereotype(stereotype) }
14
+ end
15
+
16
+ private
17
+
18
+ def run_stereotype(stereotype)
19
+ if stereotype.update_class.present?
20
+ puts stereotype.label + ': update class found. Running...'
21
+ stereotype.update_class.new(instance).run
22
+ else
23
+ puts stereotype.label + ': update class not found'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/core_ext'
4
+
5
+ module Avm
6
+ module Projects
7
+ require_sub __FILE__
8
+ end
9
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'colorized_string'
5
+
6
+ module Avm
7
+ module Projects
8
+ module Stereotype
9
+ class << self
10
+ attr_reader :stereotypes
11
+
12
+ def included(base)
13
+ @stereotypes ||= []
14
+ @stereotypes << base
15
+ base.extend(ClassMethods)
16
+ end
17
+
18
+ def git_stereotypes
19
+ stereotypes.select { |c| c.name.demodulize.downcase.match('git') }
20
+ end
21
+
22
+ def nogit_stereotypes
23
+ stereotypes - git_stereotypes
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ def label
29
+ ::ColorizedString.new(stereotype_name).send(color)
30
+ end
31
+
32
+ def stereotype_name
33
+ name.demodulize
34
+ end
35
+
36
+ {
37
+ local_project_mixin: ::Module,
38
+ publish: ::Class,
39
+ update: ::Class,
40
+ warp: ::Class
41
+ }.each do |name, is_a|
42
+ define_method "#{name}_#{is_a.name.underscore}" do
43
+ sub_constant(name.to_s.camelcase, is_a)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def sub_constant(constant_name, is_a)
50
+ constant = const_get(constant_name)
51
+ unless is_a.if_present(true) { |v| constant.is_a?(v) }
52
+ raise("#{constant} is not a #{is_a}")
53
+ end
54
+
55
+ constant
56
+ rescue NameError
57
+ nil
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/projects/stereotype'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Projects
8
+ module Stereotypes
9
+ require_sub __FILE__, base: nil
10
+
11
+ class << self
12
+ def list
13
+ @list ||= constants.map { |c| const_get(c) }
14
+ .select { |c| c.included_modules.include?(Avm::Projects::Stereotype) }
15
+ .freeze
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/projects/stereotype'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Projects
8
+ module Stereotypes
9
+ class Git
10
+ require_sub __FILE__
11
+ include Avm::Projects::Stereotype
12
+
13
+ class << self
14
+ def match?(path)
15
+ File.directory?(path.real.subpath('.git'))
16
+ end
17
+
18
+ def color
19
+ :white
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_git/local'
4
+ require 'eac_ruby_utils/core_ext'
5
+
6
+ module Avm
7
+ module Projects
8
+ module Stereotypes
9
+ class Git
10
+ module LocalProjectMixin
11
+ # @return [EacGit::Local]
12
+ def git_repo
13
+ @git_repo ||= ::EacGit::Local.new(path)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_launcher/git/publish_base'
4
+
5
+ module Avm
6
+ module Projects
7
+ module Stereotypes
8
+ class Git
9
+ class Publish < ::EacLauncher::Git::PublishBase
10
+ PUBLISH_GIT_REMOTE_NAME = 'publish'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_launcher/git/warp_base'
4
+
5
+ module Avm
6
+ module Projects
7
+ module Stereotypes
8
+ class Git
9
+ class Warp < ::EacLauncher::Git::WarpBase
10
+ private
11
+
12
+ def current_ref
13
+ 'HEAD'
14
+ end
15
+
16
+ def source_instance
17
+ instance
18
+ end
19
+
20
+ def source_remote_name
21
+ ::Avm::Projects::Stereotypes::Git::Publish::PUBLISH_GIT_REMOTE_NAME
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_launcher/git/error'
4
+ require 'eac_ruby_utils/core_ext'
5
+ require 'avm/projects/stereotype'
6
+
7
+ module Avm
8
+ module Projects
9
+ module Stereotypes
10
+ class GitSubrepo
11
+ require_sub __FILE__
12
+ include Avm::Projects::Stereotype
13
+
14
+ class << self
15
+ def match?(path)
16
+ File.exist?(path.real.subpath('.gitrepo')) && subrepo_url(path.real) != 'none'
17
+ end
18
+
19
+ def color
20
+ :light_cyan
21
+ end
22
+
23
+ def subrepo_url(path)
24
+ File.read(path.subpath('.gitrepo')).each_line do |l|
25
+ m = /remote\s*=\s(.+)/.match(l)
26
+ return m[1] if m
27
+ end
28
+ raise ::EacLauncher::Git::Error.new(path, '"remote = ... " not found')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_launcher/git/publish_base'
4
+
5
+ module Avm
6
+ module Projects
7
+ module Stereotypes
8
+ class GitSubrepo
9
+ class Publish < ::EacLauncher::Git::PublishBase
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/simple_cache'
4
+ require 'eac_launcher/git/sub_warp_base'
5
+ require 'eac_launcher/instances/error'
6
+ require 'eac_launcher/paths/real'
7
+ require 'eac_launcher/vendor/github'
8
+
9
+ module Avm
10
+ module Projects
11
+ module Stereotypes
12
+ class GitSubrepo
13
+ class Warp < ::EacLauncher::Paths::Real
14
+ include ::EacLauncher::Git::SubWarpBase
15
+ include ::EacRubyUtils::SimpleCache
16
+
17
+ attr_reader :instance
18
+
19
+ def initialize(instance)
20
+ @instance = instance
21
+ check_parent
22
+ init_aux
23
+ push_to_aux
24
+ reset
25
+ assert_target_remote
26
+ super(warped_git)
27
+ end
28
+
29
+ private
30
+
31
+ def check_parent
32
+ return if parent_git_warped.rev_parse(subrepo_parent_hash) &&
33
+ parent_git_warped.descendant?('HEAD', subrepo_parent_hash)
34
+
35
+ raise EacLauncher::Instances::Error, "Subrepo parent hash \"#{subrepo_parent_hash}\""\
36
+ " not found in \"#{parent_git_warped}\""
37
+ end
38
+
39
+ def subrepo_parent_hash
40
+ data = parent_git_warped.subrepo_status(to_parent_git_path)
41
+ h = data['Pull Parent']
42
+ return h if h.present?
43
+
44
+ raise EacLauncher::Instances::Error, "Subrepo parent hash is blank: #{data}"
45
+ end
46
+
47
+ def init_aux
48
+ ::EacLauncher::Git::Base.new(aux_path).init_bare
49
+ end
50
+
51
+ def parent_git_warped_uncached
52
+ ::EacLauncher::Git::Base.new(parent_instance.warped)
53
+ end
54
+
55
+ def aux_path
56
+ instance.cache_path('subrepo_aux_git_repository')
57
+ end
58
+
59
+ def warped_git_uncached
60
+ ::EacLauncher::Git::Base.new(instance.cache_path('git_repository'))
61
+ end
62
+
63
+ def push_to_aux
64
+ parent_git_warped.execute!('subrepo', 'branch', to_parent_git_path, '-fF')
65
+ h = parent_git_warped.rev_parse("subrepo/#{to_parent_git_path}", true)
66
+ parent_git_warped.execute!('push', aux_path, "#{h}:refs/heads/master", '--force')
67
+ end
68
+
69
+ def reset
70
+ ::EacLauncher::Git::MirrorUpdate.new(warped_git, aux_path, 'master')
71
+ end
72
+
73
+ def assert_target_remote
74
+ warped_git.assert_remote_url(::EacLauncher::Git::WarpBase::TARGET_REMOTE,
75
+ target_remote_url)
76
+ end
77
+
78
+ def target_remote_url
79
+ ::EacLauncher::Vendor::Github.to_ssh_url(
80
+ parent_git_warped.subrepo_remote_url(to_parent_git_path)
81
+ )
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'avm/projects/stereotype'
4
+ require 'avm/projects/stereotypes/git'
5
+ require 'eac_ruby_utils/core_ext'
6
+
7
+ module Avm
8
+ module Projects
9
+ module Stereotypes
10
+ class GitSubtree
11
+ require_sub __FILE__
12
+ include Avm::Projects::Stereotype
13
+
14
+ class << self
15
+ def match?(path)
16
+ return false if other_git_stereotype?(path)
17
+ return false unless other_nogit_stereotype?(path)
18
+
19
+ parent = parent_git(path.parent_path)
20
+ return false unless parent
21
+
22
+ ::Git.open(parent.real).remote(path.real.basename).url ? true : false
23
+ end
24
+
25
+ def color
26
+ :green
27
+ end
28
+
29
+ def parent_git(parent_path)
30
+ return nil unless parent_path
31
+
32
+ if ::Avm::Projects::Stereotypes::Git.match?(parent_path)
33
+ parent_path
34
+ else
35
+ parent_git(parent_path.parent_path)
36
+ end
37
+ end
38
+
39
+ def other_git_stereotype?(path)
40
+ ::Avm::Projects::Stereotypes::Git.match?(path) ||
41
+ ::Avm::Projects::Stereotypes::GitSubrepo.match?(path)
42
+ end
43
+
44
+ def other_nogit_stereotype?(path)
45
+ Avm::Projects::Stereotype.nogit_stereotypes.any? { |s| s.match?(path) }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end