super 0.0.4 → 0.0.9

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +13 -0
  3. data/CONTRIBUTING.md +56 -0
  4. data/README.md +60 -85
  5. data/STABILITY.md +50 -0
  6. data/app/assets/javascripts/super/application.js +1169 -359
  7. data/app/assets/stylesheets/super/application.css +86648 -30707
  8. data/app/controllers/super/application_controller.rb +44 -71
  9. data/app/views/layouts/super/application.html.erb +26 -6
  10. data/app/views/super/application/{_resources_header.html.erb → _collection_header.html.erb} +5 -6
  11. data/app/views/super/application/_display_rich_text.html.erb +1 -0
  12. data/app/views/super/application/_filter.html.erb +14 -0
  13. data/app/views/super/application/_filter_type_select.html.erb +31 -0
  14. data/app/views/super/application/_filter_type_text.html.erb +22 -0
  15. data/app/views/super/application/_filter_type_timestamp.html.erb +35 -0
  16. data/app/views/super/application/_flash.html.erb +13 -13
  17. data/app/views/super/application/_form_field__destroy.html.erb +6 -2
  18. data/app/views/super/application/_form_field_checkbox.html.erb +15 -0
  19. data/app/views/super/application/_form_field_generic.html.erb +19 -0
  20. data/app/views/super/application/_form_field_rich_text_area.html.erb +13 -0
  21. data/app/views/super/application/_form_field_select.html.erb +11 -5
  22. data/app/views/super/application/_form_field_text.html.erb +13 -5
  23. data/app/views/super/application/_form_has_many.html.erb +3 -3
  24. data/app/views/super/application/_form_inline_errors.html.erb +1 -1
  25. data/app/views/super/application/{_resource_header.html.erb → _member_header.html.erb} +6 -6
  26. data/app/views/super/application/_super_layout.html.erb +12 -17
  27. data/app/views/super/application/_super_pagination.html.erb +16 -0
  28. data/app/views/super/application/_super_panel.html.erb +3 -7
  29. data/app/views/super/application/_super_schema_display_actions.html.erb +5 -0
  30. data/app/views/super/application/_super_schema_display_index.html.erb +24 -0
  31. data/app/views/super/application/_super_schema_display_show.html.erb +8 -0
  32. data/app/views/super/application/{_form.html.erb → _super_schema_form.html.erb} +2 -4
  33. data/app/views/super/application/edit.html.erb +1 -1
  34. data/app/views/super/application/index.html.erb +1 -1
  35. data/app/views/super/application/new.html.erb +1 -1
  36. data/app/views/super/application/show.html.erb +1 -1
  37. data/app/views/super/feather/{_chevron_down.svg → _chevron_down.html} +0 -0
  38. data/config/locales/en.yml +5 -0
  39. data/docs/README.md +8 -0
  40. data/docs/action_text.md +48 -0
  41. data/docs/cheat.md +41 -0
  42. data/docs/faq.md +44 -0
  43. data/docs/installation.md +21 -0
  44. data/docs/quick_start.md +30 -0
  45. data/docs/webpacker.md +25 -0
  46. data/docs/yard_customizations.rb +41 -0
  47. data/frontend/super-frontend/build.js +1 -1
  48. data/frontend/super-frontend/dist/application.css +86648 -30707
  49. data/frontend/super-frontend/dist/application.js +1169 -359
  50. data/frontend/super-frontend/package.json +2 -3
  51. data/frontend/super-frontend/src/javascripts/super/{application.ts → application.js} +5 -8
  52. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.js +17 -0
  53. data/frontend/super-frontend/src/javascripts/super/{toggle_pending_destruction_controller.ts → toggle_pending_destruction_controller.js} +2 -2
  54. data/frontend/super-frontend/tailwind.config.js +7 -1
  55. data/frontend/super-frontend/yarn.lock +1368 -1391
  56. data/lib/generators/super/action_text/USAGE +23 -0
  57. data/lib/generators/super/action_text/action_text_generator.rb +30 -0
  58. data/lib/generators/super/action_text/templates/pack_super_action_text.css +23 -0
  59. data/lib/generators/super/action_text/templates/pack_super_action_text.js +4 -0
  60. data/lib/generators/super/install/install_generator.rb +16 -0
  61. data/lib/generators/super/resource/templates/resources_controller.rb.tt +1 -31
  62. data/lib/generators/super/webpacker/USAGE +5 -4
  63. data/lib/generators/super/webpacker/webpacker_generator.rb +10 -1
  64. data/lib/super.rb +22 -5
  65. data/lib/super/action_inquirer.rb +2 -2
  66. data/lib/super/assets.rb +112 -38
  67. data/lib/super/client_error.rb +43 -0
  68. data/lib/super/compatibility.rb +25 -0
  69. data/lib/super/configuration.rb +21 -69
  70. data/lib/super/controls.rb +9 -257
  71. data/lib/super/controls/optional.rb +79 -0
  72. data/lib/super/controls/required.rb +13 -0
  73. data/lib/super/controls/steps.rb +114 -0
  74. data/lib/super/display.rb +66 -3
  75. data/lib/super/display/guesser.rb +34 -0
  76. data/lib/super/display/schema_types.rb +61 -25
  77. data/lib/super/engine.rb +7 -1
  78. data/lib/super/error.rb +8 -9
  79. data/lib/super/filter.rb +12 -0
  80. data/lib/super/filter/form_object.rb +97 -0
  81. data/lib/super/filter/guesser.rb +30 -0
  82. data/lib/super/filter/operator.rb +103 -0
  83. data/lib/super/filter/plugin.rb +47 -0
  84. data/lib/super/filter/schema_types.rb +112 -0
  85. data/lib/super/form.rb +35 -0
  86. data/lib/super/form/builder.rb +48 -0
  87. data/lib/super/form/guesser.rb +27 -0
  88. data/lib/super/form/schema_types.rb +29 -22
  89. data/lib/super/form/strong_params.rb +29 -0
  90. data/lib/super/layout.rb +28 -0
  91. data/lib/super/link.rb +55 -32
  92. data/lib/super/pagination.rb +55 -0
  93. data/lib/super/panel.rb +13 -0
  94. data/lib/super/partial.rb +12 -0
  95. data/lib/super/partial/resolving.rb +24 -0
  96. data/lib/super/plugin.rb +34 -63
  97. data/lib/super/schema.rb +12 -22
  98. data/lib/super/schema/common.rb +25 -0
  99. data/lib/super/schema/guesser.rb +77 -0
  100. data/lib/super/version.rb +1 -1
  101. data/lib/super/view_helper.rb +43 -0
  102. metadata +138 -41
  103. data/app/helpers/super/application_helper.rb +0 -32
  104. data/app/views/super/application/_index.html.erb +0 -45
  105. data/app/views/super/application/_show.html.erb +0 -10
  106. data/frontend/super-frontend/src/javascripts/super/apply_template_controller.ts +0 -21
  107. data/frontend/super-frontend/src/javascripts/super/rails__ujs.d.ts +0 -1
  108. data/frontend/super-frontend/tsconfig.json +0 -13
  109. data/lib/super/action.rb +0 -22
  110. data/lib/super/action/step.rb +0 -36
  111. data/lib/super/test_support/copy_app_templates/controllers/favorite_things_controller.rb +0 -50
  112. data/lib/super/test_support/copy_app_templates/controllers/members_controller.rb +0 -74
  113. data/lib/super/test_support/copy_app_templates/controllers/ships_controller.rb +0 -47
  114. data/lib/super/test_support/copy_app_templates/migrations/20190216224956_create_members.rb +0 -11
  115. data/lib/super/test_support/copy_app_templates/migrations/20190803143320_create_ships.rb +0 -11
  116. data/lib/super/test_support/copy_app_templates/migrations/20190806014121_add_ship_to_members.rb +0 -5
  117. data/lib/super/test_support/copy_app_templates/migrations/20191126050453_create_favorite_things.rb +0 -10
  118. data/lib/super/test_support/copy_app_templates/models/favorite_thing.rb +0 -7
  119. data/lib/super/test_support/copy_app_templates/models/member.rb +0 -23
  120. data/lib/super/test_support/copy_app_templates/models/ship.rb +0 -3
  121. data/lib/super/test_support/copy_app_templates/routes.rb +0 -11
  122. data/lib/super/test_support/copy_app_templates/seeds.rb +0 -2
  123. data/lib/super/test_support/copy_app_templates/views/members/_favorite_things.html.erb +0 -11
  124. data/lib/super/test_support/fixtures/favorite_things.yml +0 -9
  125. data/lib/super/test_support/fixtures/members.yml +0 -336
  126. data/lib/super/test_support/fixtures/ships.yml +0 -10
  127. data/lib/super/test_support/generate_copy_app.rb +0 -42
  128. data/lib/super/test_support/generate_dummy.rb +0 -93
  129. data/lib/super/test_support/starfleet_seeder.rb +0 -50
  130. data/lib/tasks/super_tasks.rake +0 -4
@@ -0,0 +1,23 @@
1
+ Description:
2
+ Creates the necessary files for Super to support ActionText
3
+
4
+ Prerequisites:
5
+
6
+ * Webpacker must be installed on your Rails app
7
+ * Run: bundle exec rails webpacker:install
8
+
9
+ * ActionText must be installed on your Rails app
10
+ * Run: bundle exec rails action_text:install
11
+
12
+ Note that Super does NOT need to be configured to use Webpacker. But if you
13
+ do use Super with Webpacker, you can merge `action_text.js` into
14
+ `application.js.erb` file.
15
+
16
+ Example:
17
+ bin/rails webpacker:install # if you haven't already
18
+ bin/rails action_text:install # if you haven't already
19
+ bin/rails generate super:action_text
20
+
21
+ This will:
22
+ Create app/javascript/packs/super/action_text.js
23
+ Modify config/initializers/super.rb
@@ -0,0 +1,30 @@
1
+ module Super
2
+ class ActionTextGenerator < Rails::Generators::Base
3
+ source_root File.expand_path("templates", __dir__)
4
+
5
+ def copy_the_pack_files
6
+ template(
7
+ "pack_super_action_text.js",
8
+ "app/javascript/packs/super/action_text.js"
9
+ )
10
+ template(
11
+ "pack_super_action_text.css",
12
+ "app/javascript/packs/super/action_text.css"
13
+ )
14
+ end
15
+
16
+ def add_action_text_assets_to_config
17
+ insert_into_file(
18
+ "config/initializers/super.rb",
19
+ " c.javascripts.push(Super::Assets.webpacker(\"super/action_text\"))\n" \
20
+ " c.stylesheets.push(Super::Assets.webpacker(\"super/action_text\"))\n",
21
+ before: /\bend\b/
22
+ )
23
+ end
24
+
25
+ def remind_about_webpacker
26
+ say "Make sure Webpacker is set up on your application!", :bold
27
+ say "Run if needed: bundle exec rails webpacker:install"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ @import "trix/dist/trix";
2
+
3
+ /* This is the SCSS that the ActionText installation generator adds, compiled to CSS */
4
+ .trix-content .attachment-gallery > action-text-attachment,
5
+ .trix-content .attachment-gallery > .attachment {
6
+ flex: 1 0 33%;
7
+ padding: 0 0.5em;
8
+ max-width: 33%; }
9
+ .trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment,
10
+ .trix-content .attachment-gallery.attachment-gallery--2 > .attachment,
11
+ .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment,
12
+ .trix-content .attachment-gallery.attachment-gallery--4 > .attachment {
13
+ flex-basis: 50%;
14
+ max-width: 50%; }
15
+ .trix-content action-text-attachment .attachment {
16
+ padding: 0 !important;
17
+ max-width: 100% !important; }
18
+
19
+ /* This is for getting Tailwind to work with Trix */
20
+ .trix-content ul {
21
+ list-style-type: disc; }
22
+ .trix-content ol {
23
+ list-style-type: decimal; }
@@ -0,0 +1,4 @@
1
+ import * as ActiveStorage from "@rails/activestorage"
2
+ ActiveStorage.start()
3
+ require("trix")
4
+ require("@rails/actiontext")
@@ -29,6 +29,22 @@ module Super
29
29
  create_file("app/controllers/#{controller_namespace}/.keep", "")
30
30
  end
31
31
 
32
+ def copy_cheatsheet
33
+ super_path = Pathname.new(Gem.loaded_specs["super"].full_gem_path).expand_path
34
+ super_cheat_path = super_path.join("docs", "cheat.md")
35
+
36
+ path =
37
+ if options["controller_namespace"].present?
38
+ "app/controllers/#{controller_namespace}/README.md"
39
+ else
40
+ "app/controllers/README.md"
41
+ end
42
+
43
+ create_file(path) do
44
+ super_cheat_path.read.sub(%r{<!--.*?-->}m, "").strip + "\n"
45
+ end
46
+ end
47
+
32
48
  def setup_sprockets4_manifest
33
49
  append_to_file "app/assets/config/manifest.js", "//= link super_manifest.js\n"
34
50
  end
@@ -6,40 +6,10 @@ class <%= class_name.pluralize %>Controller < <%= parent_controller_name %>
6
6
  Controls.new
7
7
  end
8
8
 
9
- class Controls
10
- def title
11
- <%= class_name %>.name.pluralize
12
- end
13
-
9
+ class Controls < Super::Controls
14
10
  def model
15
11
  <%= class_name %>
16
12
  end
17
-
18
- def scope(action:)
19
- if action.read?
20
- <%= class_name %>.all
21
- else
22
- <%= class_name %>.all
23
- end
24
- end
25
-
26
- def permitted_params(params, action:)
27
- if action.create?
28
- params.require(:<%= file_name %>).permit()
29
- else
30
- params.require(:<%= file_name %>).permit()
31
- end
32
- end
33
-
34
- def display_schema(action:)
35
- Super::Schema.new(Super::Display::SchemaTypes.new) do |fields, type|
36
- end
37
- end
38
-
39
- def form_schema(action:)
40
- Super::Schema.new(Super::Form::SchemaTypes.new) do |fields, type|
41
- end
42
- end
43
13
  end
44
14
  end
45
15
  <% end -%>
@@ -1,14 +1,15 @@
1
1
  Description:
2
2
  Creates the necessary files for assets to be served under Webpacker
3
3
 
4
- Webpacker support requires ERB support. There are no other dependencies.
4
+ Webpacker support requires that you install ERB support for Webpacker.
5
5
 
6
6
  Installing ERB on webpacker is usually done by running the command:
7
7
  `rails webpacker:install:erb`
8
8
 
9
9
  Example:
10
10
  rails webpacker:install:erb # if you hadn't set up ERB already
11
- rails generate super:install:webpacker
11
+ rails generate super:webpacker
12
12
 
13
- This will create:
14
- app/javascript/packs/super/application.js.erb
13
+ This will:
14
+ Create app/javascript/packs/super/application.js.erb
15
+ Modify config/initializers/super.rb
@@ -9,9 +9,18 @@ module Super
9
9
  )
10
10
  end
11
11
 
12
+ def set_asset_handler_to_webpacker
13
+ insert_into_file(
14
+ "config/initializers/super.rb",
15
+ " c.javascripts = Super::Assets.use_webpacker(c.javascripts)\n" \
16
+ " c.stylesheets = Super::Assets.use_webpacker(c.stylesheets)\n",
17
+ before: /\bend\b/
18
+ )
19
+ end
20
+
12
21
  def remind_about_erb
13
22
  say "Make sure ERB is set up for Webpacker!", :bold
14
- say "Run if needed: rails webpacker:install:erb", :bold
23
+ say "Run if needed: bundle exec rails webpacker:install:erb"
15
24
  end
16
25
  end
17
26
  end
@@ -1,17 +1,30 @@
1
1
  require "tsort"
2
- require "active_support/concern"
3
2
 
4
- require "super/action"
5
- require "super/action/step"
3
+ require "rails/engine"
4
+
5
+ require "super/schema/common"
6
+
6
7
  require "super/action_inquirer"
7
8
  require "super/assets"
9
+ require "super/client_error"
10
+ require "super/compatibility"
8
11
  require "super/configuration"
9
12
  require "super/controls"
10
- require "super/engine"
11
- require "super/error"
12
13
  require "super/display"
14
+ require "super/display/guesser"
13
15
  require "super/display/schema_types"
16
+ require "super/error"
17
+ require "super/filter"
18
+ require "super/filter/form_object"
19
+ require "super/filter/guesser"
20
+ require "super/filter/operator"
21
+ require "super/filter/plugin"
22
+ require "super/filter/schema_types"
23
+ require "super/form"
24
+ require "super/form/builder"
25
+ require "super/form/guesser"
14
26
  require "super/form/schema_types"
27
+ require "super/form/strong_params"
15
28
  require "super/layout"
16
29
  require "super/link"
17
30
  require "super/navigation/automatic"
@@ -20,4 +33,8 @@ require "super/panel"
20
33
  require "super/partial"
21
34
  require "super/plugin"
22
35
  require "super/schema"
36
+ require "super/schema/guesser"
23
37
  require "super/version"
38
+ require "super/view_helper"
39
+
40
+ require "super/engine"
@@ -1,7 +1,7 @@
1
1
  module Super
2
2
  # ```ruby
3
3
  # action = Super::ActionInquirer.new(
4
- # Super::ActionInquirer.default_resources,
4
+ # Super::ActionInquirer.default_for_resources,
5
5
  # :index
6
6
  # )
7
7
  #
@@ -15,7 +15,7 @@ module Super
15
15
 
16
16
  # @return [Hash<Symbol, Array<Symbol>>] default settings for initialization
17
17
  #
18
- def self.default_resources
18
+ def self.default_for_resources
19
19
  {
20
20
  read: %i[index show new edit],
21
21
  write: %i[create update destroy],
@@ -1,8 +1,64 @@
1
1
  module Super
2
2
  # Utilities for determining whether to use Sprockets or Webpacker
3
3
  class Assets
4
- def self.sprockets_available?
5
- Gem::Dependency.new("sprockets").matching_specs.any?
4
+ def self.webpacker(path, **arguments)
5
+ Asset.new(handler: Handler.webpacker, path: path, arguments: arguments)
6
+ end
7
+
8
+ def self.sprockets(path, **arguments)
9
+ Asset.new(handler: Handler.sprockets, path: path, arguments: arguments)
10
+ end
11
+
12
+ def self.auto(path, **arguments)
13
+ Asset.new(handler: Handler.auto, path: path, arguments: arguments)
14
+ end
15
+
16
+ def self.use_webpacker(assets, grep: nil)
17
+ assets = [assets] if !assets.kind_of?(Array)
18
+
19
+ assets.map do |asset|
20
+ grep_matches = grep && asset === grep
21
+ if grep_matches || !grep
22
+ asset.instance_variable_set(:@handler, Handler.webpacker)
23
+ end
24
+
25
+ asset
26
+ end
27
+ end
28
+
29
+ def self.use_sprockets(assets, grep: nil)
30
+ assets = [assets] if !assets.kind_of?(Array)
31
+
32
+ assets.map do |asset|
33
+ grep_matches = grep && asset === grep
34
+ if grep_matches || !grep
35
+ asset.instance_variable_set(:@asset_handler, Handler.sprockets)
36
+ end
37
+
38
+ asset
39
+ end
40
+ end
41
+
42
+ class Asset
43
+ def initialize(handler:, path:, arguments:)
44
+ @handler = handler
45
+ @path = path
46
+ @arguments = arguments
47
+ end
48
+
49
+ attr_reader :handler
50
+ attr_reader :path
51
+ attr_reader :arguments
52
+
53
+ def ===(other)
54
+ return true if path == other
55
+ return true if other.is_a?(Regexp) && path.match?(other)
56
+ return true if handler == other
57
+ return true if handler.to_sym == other
58
+ return true if handler.to_s == other
59
+
60
+ false
61
+ end
6
62
  end
7
63
 
8
64
  def self.dist(gem_name, package_name)
@@ -15,49 +71,67 @@ module Super
15
71
  gem_path.join("frontend", package_name, "dist")
16
72
  end
17
73
 
18
- def self.auto
19
- @auto ||=
20
- if Gem::Dependency.new("sprockets", "~> 4.0").matching_specs.any?
21
- sprockets
22
- elsif Gem::Dependency.new("sprockets", "~> 3.0").matching_specs.any?
23
- sprockets
24
- elsif Gem::Dependency.new("sprockets", "~> 2.0").matching_specs.any?
25
- sprockets
26
- elsif Gem::Dependency.new("webpacker", "~> 4.0").matching_specs.any?
27
- webpacker
28
- elsif Gem::Dependency.new("webpacker", "~> 3.0").matching_specs.any?
29
- webpacker
30
- else
31
- none
32
- end
33
- end
74
+ class Handler
75
+ def self.auto
76
+ @auto ||=
77
+ if Gem::Dependency.new("sprockets", "~> 4.0").matching_specs.any?
78
+ sprockets
79
+ elsif Gem::Dependency.new("sprockets", "~> 3.0").matching_specs.any?
80
+ sprockets
81
+ elsif Gem::Dependency.new("sprockets", "~> 2.0").matching_specs.any?
82
+ sprockets
83
+ elsif Gem::Dependency.new("webpacker", "~> 6.0").matching_specs.any?
84
+ webpacker
85
+ elsif Gem::Dependency.new("webpacker", "~> 5.0").matching_specs.any?
86
+ webpacker
87
+ elsif Gem::Dependency.new("webpacker", "~> 4.0").matching_specs.any?
88
+ webpacker
89
+ elsif Gem::Dependency.new("webpacker", "~> 3.0").matching_specs.any?
90
+ webpacker
91
+ else
92
+ none
93
+ end
94
+ end
34
95
 
35
- def self.sprockets
36
- new(:sprockets)
37
- end
96
+ def self.sprockets_available?
97
+ Gem::Dependency.new("sprockets").matching_specs.any? && defined?(Sprockets)
98
+ end
38
99
 
39
- def self.webpacker
40
- new(:webpacker)
41
- end
100
+ def self.sprockets
101
+ @sprockets ||= new(:sprockets)
102
+ end
42
103
 
43
- def self.none
44
- new(:none)
45
- end
104
+ def self.webpacker
105
+ @webpacker ||= new(:webpacker)
106
+ end
46
107
 
47
- def initialize(asset_handler)
48
- @asset_handler = asset_handler
49
- end
108
+ def self.none
109
+ @none ||= new(:none)
110
+ end
50
111
 
51
- def sprockets?
52
- @asset_handler == :sprockets
53
- end
112
+ def initialize(asset_handler)
113
+ @asset_handler = asset_handler
114
+ end
54
115
 
55
- def webpacker?
56
- @asset_handler == :webpacker
57
- end
116
+ def sprockets?
117
+ @asset_handler == :sprockets
118
+ end
119
+
120
+ def webpacker?
121
+ @asset_handler == :webpacker
122
+ end
123
+
124
+ def none?
125
+ @asset_handler == :none
126
+ end
127
+
128
+ def to_sym
129
+ @asset_handler
130
+ end
58
131
 
59
- def none?
60
- @asset_handler == :none
132
+ def to_s
133
+ @asset_handler.to_s
134
+ end
61
135
  end
62
136
  end
63
137
  end
@@ -0,0 +1,43 @@
1
+ module Super
2
+ # A container class for all user-facing (4xx) errors thrown by this library.
3
+ #
4
+ # See also `Super::Error`
5
+ class ClientError < StandardError
6
+ class BadRequest < ClientError; end
7
+ class Unauthorized < ClientError; end
8
+ class Forbidden < ClientError; end
9
+ class NotFound < ClientError; end
10
+ class UnprocessableEntity < ClientError; end
11
+
12
+ module Handling
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ rescue_from ::Super::ClientError do |exception|
17
+ code, default_message =
18
+ case exception
19
+ when ClientError::UnprocessableEntity
20
+ [422, "Unprocessable entity"]
21
+ when ClientError::NotFound
22
+ [404, "Not found"]
23
+ when ClientError::Forbidden
24
+ [403, "Forbidden"]
25
+ when ClientError::Unauthorized
26
+ [401, "Unauthorized"]
27
+ when ClientError::BadRequest, ClientError
28
+ [400, "Bad request"]
29
+ end
30
+
31
+ flash.now.alert =
32
+ if exception.message == exception.class.name.to_s
33
+ default_message
34
+ else
35
+ exception.message
36
+ end
37
+
38
+ render "nothing", status: code
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end