templet_rails 0.1.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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +1059 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/lib/generators/templet/controller/USAGE +13 -0
  11. data/lib/generators/templet/controller/controller_generator.rb +106 -0
  12. data/lib/generators/templet/core/USAGE +23 -0
  13. data/lib/generators/templet/core/core_generator.rb +128 -0
  14. data/lib/generators/templet/core_rspec/USAGE +16 -0
  15. data/lib/generators/templet/core_rspec/core_rspec_generator.rb +65 -0
  16. data/lib/generators/templet/destroy/USAGE +19 -0
  17. data/lib/generators/templet/destroy/destroy_generator.rb +126 -0
  18. data/lib/generators/templet/routes/USAGE +14 -0
  19. data/lib/generators/templet/routes/routes_generator.rb +64 -0
  20. data/lib/generators/templet/rspec/USAGE +10 -0
  21. data/lib/generators/templet/rspec/rspec_generator.rb +124 -0
  22. data/lib/generators/templet/scaffold/USAGE +22 -0
  23. data/lib/generators/templet/scaffold/scaffold_generator.rb +75 -0
  24. data/lib/generators/templet/shared/actions_option.rb +24 -0
  25. data/lib/generators/templet/shared/add_routes_option.rb +19 -0
  26. data/lib/generators/templet/shared/child_option.rb +23 -0
  27. data/lib/generators/templet/shared/comment_tests_option.rb +19 -0
  28. data/lib/generators/templet/shared/core_helpers.rb +48 -0
  29. data/lib/generators/templet/shared/grand_parent_option.rb +20 -0
  30. data/lib/generators/templet/shared/model_fields.rb +55 -0
  31. data/lib/generators/templet/shared/model_option.rb +25 -0
  32. data/lib/generators/templet/shared/parent_option.rb +20 -0
  33. data/lib/generators/templet/templates/controller.rb.erb +108 -0
  34. data/lib/generators/templet/templates/core/app/base_viewer.rb +7 -0
  35. data/lib/generators/templet/templates/core/app/link_sets/navbar.rb +12 -0
  36. data/lib/generators/templet/templates/core/app/panel/flash_messages.rb +33 -0
  37. data/lib/generators/templet/templates/core/app/panel/layout_base.rb +40 -0
  38. data/lib/generators/templet/templates/core/app/panel/layout_header.rb +22 -0
  39. data/lib/generators/templet/templates/core/app/panel/layout_header_sidebar.rb +18 -0
  40. data/lib/generators/templet/templates/core/app/panel/nav.rb +55 -0
  41. data/lib/generators/templet/templates/core/app/panel/nav_args_option.rb +30 -0
  42. data/lib/generators/templet/templates/core/app/panel/options_config.rb +19 -0
  43. data/lib/generators/templet/templates/core/app/panel/show_parents_option.rb +22 -0
  44. data/lib/generators/templet/templates/core/app/panel/sidebar_links_option.rb +24 -0
  45. data/lib/generators/templet/templates/core/controllers/.keep +0 -0
  46. data/lib/generators/templet/templates/core/controllers/json_rendering_helpers.rb +12 -0
  47. data/lib/generators/templet/templates/core/controllers/rendering_helpers.rb +53 -0
  48. data/lib/generators/templet/templates/core/controllers/viewer_call_string.rb +144 -0
  49. data/lib/generators/templet/templates/core/controllers/viewer_call_string_class.rb +65 -0
  50. data/lib/generators/templet/templates/core/controllers/viewer_responders.rb +121 -0
  51. data/lib/generators/templet/templates/core/helpers/app.rb +4 -0
  52. data/lib/generators/templet/templates/core/helpers/templet_helper.rb +10 -0
  53. data/lib/generators/templet/templates/core/spec/support/apis/api_helper.rb +13 -0
  54. data/lib/generators/templet/templates/core/spec/support/apis/shared_examples_a_json_controller.rb +196 -0
  55. data/lib/generators/templet/templates/core/spec/support/core/model_parent_helpers.rb +36 -0
  56. data/lib/generators/templet/templates/core/spec/support/core/rest_link_procs_assignments.rb +43 -0
  57. data/lib/generators/templet/templates/core/spec/support/core/rest_link_procs_helpers.rb +15 -0
  58. data/lib/generators/templet/templates/core/spec/support/viewer/partial_test_helpers.rb +32 -0
  59. data/lib/generators/templet/templates/core/spec/support/viewer/shared_examples_a_viewer.rb +105 -0
  60. data/lib/generators/templet/templates/core/spec/templet/forms/bs_form_errors_spec.rb +18 -0
  61. data/lib/generators/templet/templates/core/spec/templet/forms/bs_form_spec.rb +58 -0
  62. data/lib/generators/templet/templates/core/spec/templet/layout/html_rails_spec.rb +64 -0
  63. data/lib/generators/templet/templates/core/spec/templet/links/bs_link_set_collection_spec.rb +79 -0
  64. data/lib/generators/templet/templates/core/spec/templet/links/rest_link_procs_params_spec.rb +68 -0
  65. data/lib/generators/templet/templates/core/spec/templet/links/rest_link_procs_parents_spec.rb +78 -0
  66. data/lib/generators/templet/templates/core/spec/templet/links/rest_link_procs_spec.rb +140 -0
  67. data/lib/generators/templet/templates/core/templet/constants.rb +54 -0
  68. data/lib/generators/templet/templates/core/templet/forms/bs_form.rb +73 -0
  69. data/lib/generators/templet/templates/core/templet/forms/bs_form_errors.rb +38 -0
  70. data/lib/generators/templet/templates/core/templet/forms/bs_form_field.rb +114 -0
  71. data/lib/generators/templet/templates/core/templet/forms/bs_form_group.rb +115 -0
  72. data/lib/generators/templet/templates/core/templet/layouts/html_rails.rb +36 -0
  73. data/lib/generators/templet/templates/core/templet/links.rb +28 -0
  74. data/lib/generators/templet/templates/core/templet/links/bs_btn_class.rb +61 -0
  75. data/lib/generators/templet/templates/core/templet/links/bs_link_set_base.rb +168 -0
  76. data/lib/generators/templet/templates/core/templet/links/bs_link_set_collection.rb +21 -0
  77. data/lib/generators/templet/templates/core/templet/links/bs_link_set_navigation.rb +62 -0
  78. data/lib/generators/templet/templates/core/templet/links/rest_link_procs.rb +163 -0
  79. data/lib/generators/templet/templates/core/templet/links/rest_link_procs_parent.rb +39 -0
  80. data/lib/generators/templet/templates/core/templet/links/rest_link_procs_sets.rb +30 -0
  81. data/lib/generators/templet/templates/core/templet/links/rest_link_text.rb +102 -0
  82. data/lib/generators/templet/templates/core/templet/links/rest_path.rb +90 -0
  83. data/lib/generators/templet/templates/core/templet/mixins.rb +9 -0
  84. data/lib/generators/templet/templates/core/templet/mixins/bs.rb +11 -0
  85. data/lib/generators/templet/templates/core/templet/mixins/bs/grid.rb +53 -0
  86. data/lib/generators/templet/templates/core/templet/mixins/bs/lists.rb +77 -0
  87. data/lib/generators/templet/templates/core/templet/mixins/field_procs.rb +140 -0
  88. data/lib/generators/templet/templates/core/templet/mixins/html_presenters.rb +82 -0
  89. data/lib/generators/templet/templates/core/templet/require_all.rb +45 -0
  90. data/lib/generators/templet/templates/core/templet/utils/html_search_form.rb +53 -0
  91. data/lib/generators/templet/templates/core/templet/utils/link_set_factory.rb +46 -0
  92. data/lib/generators/templet/templates/core/templet/utils/link_set_factory_wrapper.rb +53 -0
  93. data/lib/generators/templet/templates/core/templet/utils/list_model_parents.rb +27 -0
  94. data/lib/generators/templet/templates/core/templet/utils/navbar_form.rb +26 -0
  95. data/lib/generators/templet/templates/core/templet/utils/selected_wrapper.rb +40 -0
  96. data/lib/generators/templet/templates/core/templet/viewer.rb +11 -0
  97. data/lib/generators/templet/templates/core/templet/viewer/meta_model.rb +66 -0
  98. data/lib/generators/templet/templates/core/templet/viewer/meta_model_defaults.rb +56 -0
  99. data/lib/generators/templet/templates/core/templet/viewer/presenters.rb +44 -0
  100. data/lib/generators/templet/templates/core/templet/viewer/rest_actions.rb +24 -0
  101. data/lib/generators/templet/templates/core/templet/viewer_base.rb +111 -0
  102. data/lib/generators/templet/templates/core/templet/viewer_rest.rb +144 -0
  103. data/lib/generators/templet/viewer/USAGE +10 -0
  104. data/lib/generators/templet/viewer/viewer_generator.rb +86 -0
  105. data/lib/templet_rails.rb +5 -0
  106. data/lib/templet_rails/version.rb +3 -0
  107. data/templet_rails.gemspec +36 -0
  108. metadata +178 -0
@@ -0,0 +1,18 @@
1
+
2
+ require "rails_helper"
3
+
4
+ require 'templet/forms/bs_form_errors'
5
+
6
+ RSpec.describe Templet::Forms::BsFormErrors, type: :helper do
7
+ include PartialTestHelpers
8
+
9
+ let(:model) { build(:category, name: '').tap {|model| model.valid? } }
10
+
11
+ it "shows error message" do
12
+ selector = 'div[id=error_expl] ul li:first'
13
+
14
+ message = parse_html(selector, render_args: [model]).text
15
+
16
+ expect(message).to match %r(Name.+blank)
17
+ end
18
+ end
@@ -0,0 +1,58 @@
1
+
2
+ require "rails_helper"
3
+
4
+ require 'templet/forms/bs_form'
5
+
6
+ RSpec.describe Templet::Forms::BsForm, type: :helper do
7
+ include PartialTestHelpers
8
+
9
+ include ModelParentHelpers
10
+
11
+ def rest_path(model, parent=nil, **options)
12
+ Templet::Links::RestPath.new model, parent, **options.merge(scope: scope)
13
+ end
14
+
15
+ def parse_form_html(selector, model)
16
+ path = rest_path(model, parent)
17
+
18
+ html = render_partial(url: path) {}
19
+
20
+ parse_html(selector, html: html)
21
+ end
22
+
23
+ def expect_action(url_matcher, model=model())
24
+ selector = 'form[role=form]'
25
+
26
+ action = parse_form_html(selector, model)['action']
27
+
28
+ expect(action).to match url_matcher
29
+ end
30
+
31
+ def expect_method(http_method, model=model())
32
+ selector = 'input[name=_method]'
33
+
34
+ method_value = parse_form_html(selector, model)['value']
35
+
36
+ expect(method_value).to eq http_method
37
+ end
38
+
39
+ describe 'form action' do
40
+ it "has new model url" do
41
+ expect_action path_re, new_model
42
+ end
43
+
44
+ it "has persisted model url" do
45
+ expect_action path_re(suffix: '/\d+')
46
+ end
47
+ end
48
+
49
+ describe 'form method' do
50
+ it "is POST for new model" do
51
+ expect_method 'post', new_model
52
+ end
53
+
54
+ it "is PATCH for persisted model" do
55
+ expect_method 'patch'
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,64 @@
1
+
2
+ require "rails_helper"
3
+
4
+ require "nokogiri"
5
+
6
+ require 'templet/layouts/html_rails'
7
+
8
+ RSpec.describe Templet::Layouts::HtmlRails, type: :helper do
9
+ let(:layout) { Templet::Layouts::HtmlRails.new helper }
10
+
11
+ def render_layout(title: 'Mister', description: 'Nice', content: 'Guy')
12
+ layout.(title, description) { content }
13
+ end
14
+
15
+ def parse(html)
16
+ Nokogiri::HTML(html)
17
+ end
18
+
19
+ def parse_html(selector, single_return: true, html: render_layout)
20
+ method = single_return ? :at_css : :css
21
+
22
+ parse(html).send(method, selector)
23
+ end
24
+
25
+ it "includes viewport" do
26
+ content = parse_html('head meta[name=viewport]')['content']
27
+
28
+ expect(content).to match %r(width=.+initial-scale=)
29
+ end
30
+
31
+ describe 'Rails assets' do
32
+ it 'includes stylesheets' do
33
+ href = parse_html('head link[rel=stylesheet]')['href']
34
+
35
+ expect(href).to match %r(^/assets/application-.+\.css$)
36
+ end
37
+
38
+ it 'includes javascript' do
39
+ src = parse_html('head script')['src']
40
+
41
+ expect(src).to match %r(^/assets/application-.+\.js$)
42
+ end
43
+ end
44
+
45
+ describe "page params" do
46
+ it "includes title" do
47
+ title = parse_html('head title').text
48
+
49
+ expect(title).to eq("Mister")
50
+ end
51
+
52
+ it "includes description" do
53
+ description = parse_html('head meta[name=description]')['content']
54
+
55
+ expect(description).to eq("Nice")
56
+ end
57
+ end
58
+
59
+ it "includes body" do
60
+ body = parse_html('html body').text
61
+
62
+ expect(body).to eq("Guy")
63
+ end
64
+ end
@@ -0,0 +1,79 @@
1
+
2
+ require "rails_helper"
3
+
4
+ require 'templet/links'
5
+
6
+ RSpec.describe Templet::Links::BsLinkSetCollection, type: :helper do
7
+ include PartialTestHelpers
8
+
9
+ include ModelParentHelpers
10
+
11
+ def scoped_partial(*args, **options)
12
+ partial(*args, **options.merge(scope: scope)).call
13
+ end
14
+
15
+ describe 'link count' do
16
+ it "has two without parent" do
17
+ links = scoped_partial(grand_parent, nil)
18
+
19
+ expect(links.size).to eq 2
20
+ end
21
+
22
+ it "has three with parent" do
23
+ links = scoped_partial(model, parent, grand_parent_singular)
24
+
25
+ expect(links.size).to eq 3
26
+ end
27
+ end
28
+
29
+ describe 'parent link' do
30
+ it "has href to grand_parent index" do
31
+ links = scoped_partial(parent, grand_parent)
32
+
33
+ re = %r(href="#{scope and '/' + scope}/#{grand_parent_plural}")
34
+
35
+ expect(links.first).to match re
36
+ end
37
+
38
+ it "has href to parent index" do
39
+ links = scoped_partial(model, parent, grand_parent_plural.singularize)
40
+
41
+ re = path_re prefix: '.+href="', suffix: '".+',
42
+ parent: grand_parent_plural, model: parent_plural
43
+
44
+ expect(links.first).to match re
45
+ end
46
+ end
47
+
48
+ describe 'index link' do
49
+ it "has href" do
50
+ links = scoped_partial(model, parent, grand_parent_singular)
51
+
52
+ re = path_re prefix: '.+href="', suffix: '".+'
53
+
54
+ expect(links.second).to match re
55
+ end
56
+ end
57
+
58
+ describe 'new link' do
59
+ it "has href" do
60
+ links = scoped_partial(model, parent, grand_parent_singular)
61
+
62
+ re = path_re prefix: '.+href="', suffix: '/new".+'
63
+
64
+ expect(links.last).to match re
65
+ end
66
+ end
67
+
68
+ describe 'param passing' do
69
+ it "has default param" do
70
+ param = { token: 'book' }
71
+
72
+ options = { default_params: param }
73
+
74
+ links = scoped_partial(model, parent, grand_parent_singular, **options)
75
+
76
+ expect(links.first).to match %r(\?token=book)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,68 @@
1
+
2
+ require "rails_helper"
3
+
4
+ require 'templet/links'
5
+
6
+ RSpec.describe Templet::Links::RestLinkProcs, type: :helper do
7
+ include PartialTestHelpers
8
+
9
+ include RestLinkProcsHelpers
10
+ include RestLinkProcsAssignments
11
+
12
+ def link_method(action)
13
+ "#{action}_link"
14
+ end
15
+
16
+ def orphaned(with_parent)
17
+ with_parent ? parent : nil
18
+ end
19
+
20
+ describe 'Parameter passing' do
21
+ %w(index show delete).each do |action|
22
+ [ false, true ].each do |with_parent|
23
+ desc = "#{action} parameters with#{with_parent ? '' : :out} parent"
24
+
25
+ describe desc do
26
+ it 'passes direct params' do
27
+ link_proc = link_procs.send(link_method(action), params: param)
28
+
29
+ _, path = execute(link_proc, model, orphaned(with_parent))
30
+
31
+ expect(path.last).to eq param
32
+ end
33
+
34
+ it 'uses default params' do
35
+ link_proc = link_procs(param_options).send(link_method(action))
36
+
37
+ _, path = execute(link_proc, model, orphaned(with_parent))
38
+
39
+ expect(path.last).to eq default_param
40
+ end
41
+
42
+ it 'passes direct params and uses default params' do
43
+ link_procs = link_procs(param_options)
44
+
45
+ link_proc = link_procs.send(link_method(action), params: param)
46
+
47
+ _, path = execute(link_proc, model, orphaned(with_parent))
48
+
49
+ expect(path.last).to eq default_param.merge(param)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ =begin
57
+ describe 'param passing' do
58
+ it "has param" do
59
+
60
+ link = link_proc.index_link(params: param).(renderer, question, category)
61
+
62
+ expect(link).to match %r(\?.*q=jumper)
63
+
64
+ expect(link).to match %r(\?.*token=book)
65
+ end
66
+ end
67
+ =end
68
+ end
@@ -0,0 +1,78 @@
1
+
2
+ require "rails_helper"
3
+
4
+ require 'templet/links'
5
+
6
+ RSpec.describe Templet::Links::RestLinkProcs, type: :helper do
7
+ include PartialTestHelpers
8
+
9
+ include RestLinkProcsHelpers
10
+ include RestLinkProcsAssignments
11
+
12
+ describe 'parent link' do
13
+ let (:gp_opts) { { grand_parent: parent_singular } }
14
+
15
+ describe '#index' do
16
+ it 'has path of parent' do
17
+ _, path = execute(link_procs.index_parent_link, model, parent)
18
+
19
+ expect(path).to eq [parent_plural]
20
+ end
21
+
22
+ it 'has path of grand parent' do
23
+ link_proc = link_procs.index_parent_link(**gp_opts)
24
+
25
+ _, path = execute(link_proc, child, model)
26
+
27
+ expect(path).to eq [parent, model_plural]
28
+ end
29
+ end
30
+
31
+ describe '#show' do
32
+ it 'has path of parent' do
33
+ _, path = execute(link_procs.show_parent_link, model, parent)
34
+
35
+ expect(path).to eq [parent]
36
+ end
37
+
38
+ it 'has path of grand parent' do
39
+ link_proc = link_procs.show_parent_link(**gp_opts)
40
+ _, path = execute(link_proc, child, model)
41
+
42
+ expect(path).to eq [parent, model]
43
+ end
44
+ end
45
+
46
+ describe '#new' do
47
+ it 'has path of parent' do
48
+ _, path = execute(link_procs.new_parent_link, model, parent)
49
+
50
+ expect(path).to eq [:new, parent_singular]
51
+ end
52
+
53
+ it 'has path of grand parent' do
54
+ link_proc = link_procs.new_parent_link(**gp_opts)
55
+
56
+ _, path = execute(link_proc, child, model)
57
+
58
+ expect(path).to eq [:new, parent, model_singular]
59
+ end
60
+ end
61
+
62
+ describe '#edit' do
63
+ it 'has path of parent' do
64
+ _, path = execute(link_procs.edit_parent_link, model, parent)
65
+
66
+ expect(path).to eq [:edit, parent]
67
+ end
68
+
69
+ it 'has path of grand parent' do
70
+ link_proc = link_procs.edit_parent_link(**gp_opts)
71
+
72
+ _, path = execute(link_proc, child, model)
73
+
74
+ expect(path).to eq [:edit, parent, model]
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,140 @@
1
+
2
+ require "rails_helper"
3
+
4
+ require 'templet/links'
5
+
6
+ RSpec.describe Templet::Links::RestLinkProcs, type: :helper do
7
+ include PartialTestHelpers
8
+
9
+ include RestLinkProcsHelpers
10
+ include RestLinkProcsAssignments
11
+
12
+ describe 'link path' do
13
+ describe '#index' do
14
+ describe 'unnamed' do
15
+ it 'has path with model' do
16
+ _, path = execute(link_procs.index_link, model, nil)
17
+
18
+ expect([model_plural]).to eq path
19
+ expect(path).to eq [model_plural]
20
+ end
21
+
22
+ it 'has path with model and parent' do
23
+ _, path = execute(link_procs.index_link, model, parent)
24
+
25
+ expect(path).to eq [parent, model_plural]
26
+ end
27
+ end
28
+
29
+ describe 'named' do
30
+ it 'has path with no model' do
31
+ _, path = execute(link_procs.index_link(name: :dropper), nil, nil)
32
+
33
+ expect(path).to eq %i(droppers)
34
+ end
35
+
36
+ it 'has path with model' do
37
+ _, path = execute(link_procs.index_link(name: :dropper), model, nil)
38
+
39
+ expect(path).to eq [model, :droppers]
40
+ end
41
+
42
+ it 'has path with model and parent' do
43
+ _, path = execute(link_procs.index_link(name: :dropper), model, parent)
44
+
45
+ expect(path).to eq [model, :droppers]
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#show' do
51
+ it 'has path with model' do
52
+ _, path = execute(link_procs.show_link, model, nil)
53
+
54
+ expect(path).to eq single_path
55
+ end
56
+
57
+ it 'has path with model and parent' do
58
+ _, path = execute(link_procs.show_link, model, parent)
59
+
60
+ expect(path).to eq dual_path
61
+ end
62
+ end
63
+
64
+ describe '#new' do
65
+ describe 'unnamed' do
66
+ it 'has path with model' do
67
+ _, path = execute(link_procs.new_link, model, nil)
68
+
69
+ expect(path).to eq [:new, model_singular]
70
+ end
71
+
72
+ it 'has path with model and parent' do
73
+ _, path = execute(link_procs.new_link, model, parent)
74
+
75
+ expect(path).to eq [:new, parent, model_singular]
76
+ end
77
+ end
78
+
79
+ describe 'named' do
80
+ it 'has path with no model' do
81
+ _, path = execute(link_procs.new_link(name: :dropper), nil, nil)
82
+
83
+ expect(path).to eq %i(new dropper)
84
+ end
85
+
86
+ it 'has path with model' do
87
+ _, path = execute(link_procs.new_link(name: :dropper), model, nil)
88
+
89
+ expect(path).to eq [:new, model, :dropper]
90
+ end
91
+
92
+ it 'has path with model and parent' do
93
+ _, path = execute(link_procs.new_link(name: :dropper), model, parent)
94
+
95
+ expect(path).to eq [:new, model, :dropper]
96
+ end
97
+ end
98
+ end
99
+
100
+ describe '#edit' do
101
+ it 'has path with model' do
102
+ _, path = execute(link_procs.edit_link, model, nil)
103
+
104
+ expect(path).to eq %i(edit) + single_path
105
+ end
106
+
107
+ it 'has path with model and parent' do
108
+ _, path = execute(link_procs.edit_link, model, parent)
109
+
110
+ expect(path).to eq %i(edit) + dual_path
111
+ end
112
+ end
113
+
114
+ describe '#delete' do
115
+ it 'has path with model' do
116
+ _, path = execute(link_procs.delete_link, model, nil)
117
+
118
+ expect(path).to eq single_path
119
+ end
120
+
121
+ it 'has path with model and parent' do
122
+ _, path = execute(link_procs.delete_link, model, parent)
123
+
124
+ expect(path).to eq dual_path
125
+ end
126
+
127
+ it 'uses delete method' do
128
+ link, _ = execute(link_procs.delete_link, model, parent)
129
+
130
+ expect(link[2][:method]).to eq :delete
131
+ end
132
+
133
+ it 'has confirm data' do
134
+ link, _ = execute(link_procs.delete_link, model, parent)
135
+
136
+ expect(link[2][:data][:confirm]).to match /Delete/
137
+ end
138
+ end
139
+ end
140
+ end