draper_new 3.0.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 (144) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +1 -0
  6. data/CHANGELOG.md +230 -0
  7. data/CONTRIBUTING.md +20 -0
  8. data/Gemfile +16 -0
  9. data/Guardfile +26 -0
  10. data/LICENSE +7 -0
  11. data/README.md +587 -0
  12. data/Rakefile +69 -0
  13. data/draper_new.gemspec +31 -0
  14. data/gemfiles/4.0.gemfile +3 -0
  15. data/gemfiles/4.1.gemfile +3 -0
  16. data/gemfiles/4.2.6.gemfile +3 -0
  17. data/gemfiles/4.2.gemfile +3 -0
  18. data/lib/draper.rb +63 -0
  19. data/lib/draper/automatic_delegation.rb +56 -0
  20. data/lib/draper/collection_decorator.rb +100 -0
  21. data/lib/draper/decoratable.rb +96 -0
  22. data/lib/draper/decoratable/equality.rb +26 -0
  23. data/lib/draper/decorated_association.rb +35 -0
  24. data/lib/draper/decorates_assigned.rb +44 -0
  25. data/lib/draper/decorator.rb +293 -0
  26. data/lib/draper/delegation.rb +13 -0
  27. data/lib/draper/factory.rb +91 -0
  28. data/lib/draper/finders.rb +37 -0
  29. data/lib/draper/helper_proxy.rb +44 -0
  30. data/lib/draper/helper_support.rb +5 -0
  31. data/lib/draper/lazy_helpers.rb +15 -0
  32. data/lib/draper/railtie.rb +70 -0
  33. data/lib/draper/tasks/test.rake +22 -0
  34. data/lib/draper/test/devise_helper.rb +30 -0
  35. data/lib/draper/test/minitest_integration.rb +6 -0
  36. data/lib/draper/test/rspec_integration.rb +20 -0
  37. data/lib/draper/test_case.rb +42 -0
  38. data/lib/draper/undecorate.rb +9 -0
  39. data/lib/draper/version.rb +3 -0
  40. data/lib/draper/view_context.rb +104 -0
  41. data/lib/draper/view_context/build_strategy.rb +48 -0
  42. data/lib/draper/view_helpers.rb +37 -0
  43. data/lib/generators/controller_override.rb +17 -0
  44. data/lib/generators/mini_test/decorator_generator.rb +20 -0
  45. data/lib/generators/mini_test/templates/decorator_spec.rb +4 -0
  46. data/lib/generators/mini_test/templates/decorator_test.rb +4 -0
  47. data/lib/generators/rails/decorator_generator.rb +36 -0
  48. data/lib/generators/rails/templates/decorator.rb +19 -0
  49. data/lib/generators/rspec/decorator_generator.rb +9 -0
  50. data/lib/generators/rspec/templates/decorator_spec.rb +4 -0
  51. data/lib/generators/test_unit/decorator_generator.rb +9 -0
  52. data/lib/generators/test_unit/templates/decorator_test.rb +4 -0
  53. data/spec/draper/collection_decorator_spec.rb +307 -0
  54. data/spec/draper/decoratable/equality_spec.rb +10 -0
  55. data/spec/draper/decoratable_spec.rb +202 -0
  56. data/spec/draper/decorated_association_spec.rb +84 -0
  57. data/spec/draper/decorates_assigned_spec.rb +71 -0
  58. data/spec/draper/decorator_spec.rb +816 -0
  59. data/spec/draper/factory_spec.rb +251 -0
  60. data/spec/draper/finders_spec.rb +166 -0
  61. data/spec/draper/helper_proxy_spec.rb +61 -0
  62. data/spec/draper/lazy_helpers_spec.rb +21 -0
  63. data/spec/draper/undecorate_spec.rb +19 -0
  64. data/spec/draper/view_context/build_strategy_spec.rb +116 -0
  65. data/spec/draper/view_context_spec.rb +154 -0
  66. data/spec/draper/view_helpers_spec.rb +8 -0
  67. data/spec/dummy/.rspec +2 -0
  68. data/spec/dummy/Rakefile +7 -0
  69. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  70. data/spec/dummy/app/controllers/localized_urls.rb +5 -0
  71. data/spec/dummy/app/controllers/posts_controller.rb +20 -0
  72. data/spec/dummy/app/decorators/mongoid_post_decorator.rb +4 -0
  73. data/spec/dummy/app/decorators/post_decorator.rb +60 -0
  74. data/spec/dummy/app/helpers/application_helper.rb +5 -0
  75. data/spec/dummy/app/mailers/application_mailer.rb +3 -0
  76. data/spec/dummy/app/mailers/post_mailer.rb +19 -0
  77. data/spec/dummy/app/models/admin.rb +5 -0
  78. data/spec/dummy/app/models/mongoid_post.rb +5 -0
  79. data/spec/dummy/app/models/post.rb +3 -0
  80. data/spec/dummy/app/models/user.rb +5 -0
  81. data/spec/dummy/app/views/layouts/application.html.erb +11 -0
  82. data/spec/dummy/app/views/post_mailer/decorated_email.html.erb +1 -0
  83. data/spec/dummy/app/views/posts/_post.html.erb +40 -0
  84. data/spec/dummy/app/views/posts/show.html.erb +1 -0
  85. data/spec/dummy/bin/rails +4 -0
  86. data/spec/dummy/config.ru +4 -0
  87. data/spec/dummy/config/application.rb +71 -0
  88. data/spec/dummy/config/boot.rb +5 -0
  89. data/spec/dummy/config/database.yml +25 -0
  90. data/spec/dummy/config/environment.rb +5 -0
  91. data/spec/dummy/config/environments/development.rb +33 -0
  92. data/spec/dummy/config/environments/production.rb +57 -0
  93. data/spec/dummy/config/environments/test.rb +31 -0
  94. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  95. data/spec/dummy/config/initializers/inflections.rb +15 -0
  96. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  97. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  98. data/spec/dummy/config/initializers/session_store.rb +8 -0
  99. data/spec/dummy/config/locales/en.yml +5 -0
  100. data/spec/dummy/config/mongoid.yml +79 -0
  101. data/spec/dummy/config/routes.rb +9 -0
  102. data/spec/dummy/db/migrate/20121019115657_create_posts.rb +8 -0
  103. data/spec/dummy/db/schema.rb +21 -0
  104. data/spec/dummy/db/seeds.rb +2 -0
  105. data/spec/dummy/fast_spec/post_decorator_spec.rb +37 -0
  106. data/spec/dummy/lib/tasks/test.rake +16 -0
  107. data/spec/dummy/public/404.html +26 -0
  108. data/spec/dummy/public/422.html +26 -0
  109. data/spec/dummy/public/500.html +25 -0
  110. data/spec/dummy/public/favicon.ico +0 -0
  111. data/spec/dummy/script/rails +6 -0
  112. data/spec/dummy/spec/decorators/active_model_serializers_spec.rb +16 -0
  113. data/spec/dummy/spec/decorators/devise_spec.rb +64 -0
  114. data/spec/dummy/spec/decorators/helpers_spec.rb +21 -0
  115. data/spec/dummy/spec/decorators/post_decorator_spec.rb +66 -0
  116. data/spec/dummy/spec/decorators/spec_type_spec.rb +7 -0
  117. data/spec/dummy/spec/decorators/view_context_spec.rb +22 -0
  118. data/spec/dummy/spec/mailers/post_mailer_spec.rb +33 -0
  119. data/spec/dummy/spec/models/mongoid_post_spec.rb +8 -0
  120. data/spec/dummy/spec/models/post_spec.rb +6 -0
  121. data/spec/dummy/spec/shared_examples/decoratable.rb +24 -0
  122. data/spec/dummy/spec/spec_helper.rb +8 -0
  123. data/spec/dummy/test/decorators/minitest/devise_test.rb +64 -0
  124. data/spec/dummy/test/decorators/minitest/helpers_test.rb +21 -0
  125. data/spec/dummy/test/decorators/minitest/spec_type_test.rb +52 -0
  126. data/spec/dummy/test/decorators/minitest/view_context_test.rb +24 -0
  127. data/spec/dummy/test/decorators/test_unit/devise_test.rb +64 -0
  128. data/spec/dummy/test/decorators/test_unit/helpers_test.rb +21 -0
  129. data/spec/dummy/test/decorators/test_unit/view_context_test.rb +24 -0
  130. data/spec/dummy/test/minitest_helper.rb +2 -0
  131. data/spec/dummy/test/test_helper.rb +3 -0
  132. data/spec/generators/controller/controller_generator_spec.rb +22 -0
  133. data/spec/generators/decorator/decorator_generator_spec.rb +92 -0
  134. data/spec/integration/integration_spec.rb +66 -0
  135. data/spec/performance/active_record.rb +4 -0
  136. data/spec/performance/benchmark.rb +55 -0
  137. data/spec/performance/decorators.rb +45 -0
  138. data/spec/performance/models.rb +20 -0
  139. data/spec/spec_helper.rb +41 -0
  140. data/spec/support/dummy_app.rb +85 -0
  141. data/spec/support/matchers/have_text.rb +50 -0
  142. data/spec/support/shared_examples/decoratable_equality.rb +40 -0
  143. data/spec/support/shared_examples/view_helpers.rb +39 -0
  144. metadata +420 -0
@@ -0,0 +1,37 @@
1
+ module Draper
2
+ # Provides automatically-decorated finder methods for your decorators. You
3
+ # do not have to extend this module directly; it is extended by
4
+ # {Decorator.decorates_finders}.
5
+ module Finders
6
+
7
+ def find(id, options = {})
8
+ decorate(object_class.find(id), options)
9
+ end
10
+
11
+ def all(options = {})
12
+ decorate_collection(object_class.all, options)
13
+ end
14
+
15
+ def first(options = {})
16
+ decorate(object_class.first, options)
17
+ end
18
+
19
+ def last(options = {})
20
+ decorate(object_class.last, options)
21
+ end
22
+
23
+ # Decorates dynamic finder methods (`find_all_by_` and friends).
24
+ def method_missing(method, *args, &block)
25
+ return super unless method =~ /^find_(all_|last_|or_(initialize_|create_))?by_/
26
+
27
+ result = object_class.send(method, *args, &block)
28
+ options = args.extract_options!
29
+
30
+ if method =~ /^find_all/
31
+ decorate_collection(result, options)
32
+ else
33
+ decorate(result, options)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ module Draper
2
+ # Provides access to helper methods - both Rails built-in helpers, and those
3
+ # defined in your application.
4
+ class HelperProxy
5
+
6
+ # @overload initialize(view_context)
7
+ def initialize(view_context = nil)
8
+ view_context ||= current_view_context # backwards compatibility
9
+
10
+ @view_context = view_context
11
+ end
12
+
13
+ # Sends helper methods to the view context.
14
+ def method_missing(method, *args, &block)
15
+ self.class.define_proxy method
16
+ send(method, *args, &block)
17
+ end
18
+
19
+ # Checks if the context responds to an instance method, or is able to
20
+ # proxy it to the view context.
21
+ def respond_to_missing?(method, include_private = false)
22
+ super || view_context.respond_to?(method)
23
+ end
24
+
25
+ delegate :capture, to: :view_context
26
+
27
+ protected
28
+
29
+ attr_reader :view_context
30
+
31
+ private
32
+
33
+ def self.define_proxy(name)
34
+ define_method name do |*args, &block|
35
+ view_context.send(name, *args, &block)
36
+ end
37
+ end
38
+
39
+ def current_view_context
40
+ ActiveSupport::Deprecation.warn("wrong number of arguments (0 for 1) passed to Draper::HelperProxy.new", caller[1..-1])
41
+ Draper::ViewContext.current.view_context
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ module Draper::HelperSupport
2
+ def decorate(input, &block)
3
+ capture { block.call(input.decorate) }
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ module Draper
2
+ # Include this module in your decorators to get direct access to the helpers
3
+ # so that you can stop typing `h.` everywhere, at the cost of mixing in a
4
+ # bazillion methods.
5
+ module LazyHelpers
6
+
7
+ # Sends missing methods to the {HelperProxy}.
8
+ def method_missing(method, *args, &block)
9
+ helpers.send(method, *args, &block)
10
+ rescue NoMethodError
11
+ super
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,70 @@
1
+ require 'rails/railtie'
2
+
3
+ module ActiveModel
4
+ class Railtie < Rails::Railtie
5
+ generators do |app|
6
+ app ||= Rails.application # Rails 3.0.x does not yield `app`
7
+
8
+ Rails::Generators.configure! app.config.generators
9
+ require_relative '../generators/controller_override'
10
+ end
11
+ end
12
+ end
13
+
14
+ module Draper
15
+ class Railtie < Rails::Railtie
16
+
17
+ config.after_initialize do |app|
18
+ app.config.paths.add 'app/decorators', eager_load: true
19
+
20
+ if Rails.env.test?
21
+ require 'draper/test_case'
22
+ require 'draper/test/rspec_integration' if defined?(RSpec) and RSpec.respond_to?(:configure)
23
+ end
24
+ end
25
+
26
+ initializer "draper.setup_action_controller" do |app|
27
+ ActiveSupport.on_load :action_controller do
28
+ Draper.setup_action_controller self
29
+ end
30
+ end
31
+
32
+ initializer "draper.setup_action_mailer" do |app|
33
+ ActiveSupport.on_load :action_mailer do
34
+ Draper.setup_action_mailer self
35
+ end
36
+ end
37
+
38
+ initializer "draper.setup_orm" do |app|
39
+ [:active_record, :mongoid].each do |orm|
40
+ ActiveSupport.on_load orm do
41
+ Draper.setup_orm self
42
+ end
43
+ end
44
+ end
45
+
46
+ initializer "draper.setup_active_model_serializers" do |app|
47
+ ActiveSupport.on_load :active_model_serializers do
48
+ if defined?(ActiveModel::ArraySerializerSupport)
49
+ Draper::CollectionDecorator.send :include, ActiveModel::ArraySerializerSupport
50
+ end
51
+ end
52
+ end
53
+
54
+ initializer "draper.minitest-rails_integration" do |app|
55
+ ActiveSupport.on_load :minitest do
56
+ require "draper/test/minitest_integration"
57
+ end
58
+ end
59
+
60
+ console do
61
+ require 'action_controller/test_case'
62
+ ApplicationController.new.view_context
63
+ Draper::ViewContext.build
64
+ end
65
+
66
+ rake_tasks do
67
+ Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f }
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,22 @@
1
+ require 'rake/testtask'
2
+
3
+ test_task = if Rails.version.to_f < 3.2
4
+ require 'rails/test_unit/railtie'
5
+ Rake::TestTask
6
+ else
7
+ require 'rails/test_unit/sub_test_task'
8
+ Rails::SubTestTask
9
+ end
10
+
11
+ namespace :test do
12
+ test_task.new(:decorators => "test:prepare") do |t|
13
+ t.libs << "test"
14
+ t.pattern = "test/decorators/**/*_test.rb"
15
+ end
16
+ end
17
+
18
+ if Rails.version.to_f < 4.2 && Rake::Task.task_defined?('test:run')
19
+ Rake::Task['test:run'].enhance do
20
+ Rake::Task['test:decorators'].invoke
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module Draper
2
+ module DeviseHelper
3
+ def sign_in(resource_or_scope, resource = nil)
4
+ scope = begin
5
+ Devise::Mapping.find_scope!(resource_or_scope)
6
+ rescue RuntimeError => e
7
+ # Draper 1.0 didn't require the mapping to exist
8
+ ActiveSupport::Deprecation.warn("#{e.message}.\nUse `sign_in :user, mock_user` instead.", caller)
9
+ :user
10
+ end
11
+
12
+ _stub_current_scope scope, resource || resource_or_scope
13
+ end
14
+
15
+ def sign_out(resource_or_scope)
16
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
17
+ _stub_current_scope scope, nil
18
+ end
19
+
20
+ private
21
+
22
+ def _stub_current_scope(scope, resource)
23
+ Draper::ViewContext.current.controller.singleton_class.class_eval do
24
+ define_method "current_#{scope}" do
25
+ resource
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ class Draper::TestCase
2
+ register_spec_type(self) do |desc|
3
+ desc < Draper::Decorator || desc < Draper::CollectionDecorator if desc.is_a?(Class)
4
+ end
5
+ register_spec_type(/Decorator( ?Test)?\z/i, self)
6
+ end
@@ -0,0 +1,20 @@
1
+ module Draper
2
+ module DecoratorExampleGroup
3
+ include Draper::TestCase::Behavior
4
+ extend ActiveSupport::Concern
5
+
6
+ included { metadata[:type] = :decorator }
7
+ end
8
+
9
+ RSpec.configure do |config|
10
+ if RSpec::Core::Version::STRING.starts_with?("3")
11
+ config.include DecoratorExampleGroup, file_path: %r{spec/decorators}, type: :decorator
12
+ else
13
+ config.include DecoratorExampleGroup, example_group: {file_path: %r{spec/decorators}}, type: :decorator
14
+ end
15
+
16
+ [:decorator, :controller, :mailer].each do |type|
17
+ config.before(:each, type: type) { Draper::ViewContext.clear! }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ module Draper
2
+ require 'active_support/test_case'
3
+
4
+ class TestCase < ::ActiveSupport::TestCase
5
+ module ViewContextTeardown
6
+ def teardown
7
+ super
8
+ Draper::ViewContext.clear!
9
+ end
10
+ end
11
+
12
+ module Behavior
13
+ if defined?(::Devise)
14
+ require 'draper/test/devise_helper'
15
+ include Draper::DeviseHelper
16
+ end
17
+
18
+ if defined?(::Capybara) && (defined?(::RSpec) || defined?(::MiniTest::Matchers))
19
+ require 'capybara/rspec/matchers'
20
+ include ::Capybara::RSpecMatchers
21
+ end
22
+
23
+ include Draper::ViewHelpers::ClassMethods
24
+ alias_method :helper, :helpers
25
+ end
26
+
27
+ include Behavior
28
+ include ViewContextTeardown
29
+ end
30
+ end
31
+
32
+ if defined?(ActionController::TestCase)
33
+ class ActionController::TestCase
34
+ include Draper::TestCase::ViewContextTeardown
35
+ end
36
+ end
37
+
38
+ if defined?(ActionMailer::TestCase)
39
+ class ActionMailer::TestCase
40
+ include Draper::TestCase::ViewContextTeardown
41
+ end
42
+ end
@@ -0,0 +1,9 @@
1
+ module Draper
2
+ def self.undecorate(object)
3
+ if object.respond_to?(:decorated?) && object.decorated?
4
+ object.object
5
+ else
6
+ object
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Draper
2
+ VERSION = "3.0.0"
3
+ end
@@ -0,0 +1,104 @@
1
+ require 'draper/view_context/build_strategy'
2
+ require 'request_store'
3
+
4
+ module Draper
5
+ module ViewContext
6
+ # Hooks into a controller or mailer to save the view context in {current}.
7
+ def view_context
8
+ super.tap do |context|
9
+ Draper::ViewContext.current = context
10
+ end
11
+ end
12
+
13
+ # Set the current controller
14
+ def activate_draper
15
+ Draper::ViewContext.controller = self
16
+ end
17
+
18
+ # Returns the current controller.
19
+ def self.controller
20
+ RequestStore.store[:current_controller]
21
+ end
22
+
23
+ # Sets the current controller.
24
+ def self.controller=(controller)
25
+ RequestStore.store[:current_controller] = controller
26
+ end
27
+
28
+ # Returns the current view context, or builds one if none is saved.
29
+ #
30
+ # @return [HelperProxy]
31
+ def self.current
32
+ RequestStore.store.fetch(:current_view_context) { build! }
33
+ end
34
+
35
+ # Sets the current view context.
36
+ def self.current=(view_context)
37
+ RequestStore.store[:current_view_context] = Draper::HelperProxy.new(view_context)
38
+ end
39
+
40
+ # Clears the saved controller and view context.
41
+ def self.clear!
42
+ RequestStore.store.delete :current_controller
43
+ RequestStore.store.delete :current_view_context
44
+ end
45
+
46
+ # Builds a new view context for usage in tests. See {test_strategy} for
47
+ # details of how the view context is built.
48
+ def self.build
49
+ build_strategy.call
50
+ end
51
+
52
+ # Builds a new view context and sets it as the current view context.
53
+ #
54
+ # @return [HelperProxy]
55
+ def self.build!
56
+ # send because we want to return the HelperProxy returned from #current=
57
+ send :current=, build
58
+ end
59
+
60
+ # Configures the strategy used to build view contexts in tests, which
61
+ # defaults to `:full` if `test_strategy` has not been called. Evaluates
62
+ # the block, if given, in the context of the view context's class.
63
+ #
64
+ # @example Pass a block to add helper methods to the view context:
65
+ # Draper::ViewContext.test_strategy :fast do
66
+ # include ApplicationHelper
67
+ # end
68
+ #
69
+ # @param [:full, :fast] name
70
+ # the strategy to use:
71
+ #
72
+ # `:full` - build a fully-working view context. Your Rails environment
73
+ # must be loaded, including your `ApplicationController`.
74
+ #
75
+ # `:fast` - build a minimal view context in tests, with no dependencies
76
+ # on other components of your application.
77
+ def self.test_strategy(name, &block)
78
+ @build_strategy = Draper::ViewContext::BuildStrategy.new(name, &block)
79
+ end
80
+
81
+ # @private
82
+ def self.build_strategy
83
+ @build_strategy ||= Draper::ViewContext::BuildStrategy.new(:full)
84
+ end
85
+
86
+ # @deprecated Use {controller} instead.
87
+ def self.current_controller
88
+ ActiveSupport::Deprecation.warn("Draper::ViewContext.current_controller is deprecated (use controller instead)", caller)
89
+ self.controller || ApplicationController.new
90
+ end
91
+
92
+ # @deprecated Use {controller=} instead.
93
+ def self.current_controller=(controller)
94
+ ActiveSupport::Deprecation.warn("Draper::ViewContext.current_controller= is deprecated (use controller instead)", caller)
95
+ self.controller = controller
96
+ end
97
+
98
+ # @deprecated Use {build} instead.
99
+ def self.build_view_context
100
+ ActiveSupport::Deprecation.warn("Draper::ViewContext.build_view_context is deprecated (use build instead)", caller)
101
+ build
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,48 @@
1
+ module Draper
2
+ module ViewContext
3
+ # @private
4
+ module BuildStrategy
5
+
6
+ def self.new(name, &block)
7
+ const_get(name.to_s.camelize).new(&block)
8
+ end
9
+
10
+ class Fast
11
+ def initialize(&block)
12
+ @view_context_class = Class.new(ActionView::Base, &block)
13
+ end
14
+
15
+ def call
16
+ view_context_class.new
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :view_context_class
22
+ end
23
+
24
+ class Full
25
+ def initialize(&block)
26
+ @block = block
27
+ end
28
+
29
+ def call
30
+ controller.view_context.tap do |context|
31
+ context.singleton_class.class_eval(&block) if block
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :block
38
+
39
+ def controller
40
+ (Draper::ViewContext.controller || ApplicationController.new).tap do |controller|
41
+ controller.request ||= ActionController::TestRequest.new if defined?(ActionController::TestRequest)
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end