legacy_mailers 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.
Files changed (84) hide show
  1. data/README.md +29 -0
  2. data/lib/action_mailer/adv_attr_accessor.rb +28 -0
  3. data/lib/action_mailer/deprecated_api.rb +152 -0
  4. data/lib/action_mailer/old_api.rb +258 -0
  5. data/lib/legacy_mailers.rb +10 -0
  6. data/lib/legacy_mailers/version.rb +3 -0
  7. data/test/abstract_unit.rb +70 -0
  8. data/test/adv_attr_test.rb +36 -0
  9. data/test/asset_host_test.rb +56 -0
  10. data/test/fixtures/another.path/base_mailer/welcome.erb +1 -0
  11. data/test/fixtures/asset_host_mailer/email_with_asset.html.erb +1 -0
  12. data/test/fixtures/asset_mailer/welcome.html.erb +1 -0
  13. data/test/fixtures/attachments/foo.jpg +0 -0
  14. data/test/fixtures/attachments/test.jpg +0 -0
  15. data/test/fixtures/auto_layout_mailer/hello.html.erb +1 -0
  16. data/test/fixtures/auto_layout_mailer/multipart.html.erb +1 -0
  17. data/test/fixtures/auto_layout_mailer/multipart.text.erb +1 -0
  18. data/test/fixtures/base_mailer/attachment_with_content.erb +1 -0
  19. data/test/fixtures/base_mailer/different_layout.html.erb +1 -0
  20. data/test/fixtures/base_mailer/different_layout.text.erb +1 -0
  21. data/test/fixtures/base_mailer/email_custom_layout.text.html.erb +1 -0
  22. data/test/fixtures/base_mailer/email_with_translations.html.erb +1 -0
  23. data/test/fixtures/base_mailer/explicit_multipart_templates.html.erb +1 -0
  24. data/test/fixtures/base_mailer/explicit_multipart_templates.text.erb +1 -0
  25. data/test/fixtures/base_mailer/explicit_multipart_with_one_template.erb +1 -0
  26. data/test/fixtures/base_mailer/html_only.html.erb +1 -0
  27. data/test/fixtures/base_mailer/implicit_multipart.html.erb +1 -0
  28. data/test/fixtures/base_mailer/implicit_multipart.text.erb +1 -0
  29. data/test/fixtures/base_mailer/implicit_with_locale.en.html.erb +1 -0
  30. data/test/fixtures/base_mailer/implicit_with_locale.html.erb +1 -0
  31. data/test/fixtures/base_mailer/implicit_with_locale.pl.text.erb +1 -0
  32. data/test/fixtures/base_mailer/implicit_with_locale.text.erb +1 -0
  33. data/test/fixtures/base_mailer/inline_attachment.html.erb +5 -0
  34. data/test/fixtures/base_mailer/inline_attachment.text.erb +4 -0
  35. data/test/fixtures/base_mailer/plain_text_only.text.erb +1 -0
  36. data/test/fixtures/base_mailer/welcome.erb +1 -0
  37. data/test/fixtures/explicit_layout_mailer/logout.html.erb +1 -0
  38. data/test/fixtures/explicit_layout_mailer/signup.html.erb +1 -0
  39. data/test/fixtures/first_mailer/share.erb +1 -0
  40. data/test/fixtures/i18n_test_mailer/mail_with_i18n_subject.erb +4 -0
  41. data/test/fixtures/layouts/auto_layout_mailer.html.erb +1 -0
  42. data/test/fixtures/layouts/auto_layout_mailer.text.erb +1 -0
  43. data/test/fixtures/layouts/different_layout.html.erb +1 -0
  44. data/test/fixtures/layouts/different_layout.text.erb +1 -0
  45. data/test/fixtures/layouts/spam.html.erb +1 -0
  46. data/test/fixtures/nested_layout_mailer/signup.html.erb +1 -0
  47. data/test/fixtures/path.with.dots/funky_path_mailer/multipart_with_template_path_with_dots.erb +1 -0
  48. data/test/fixtures/raw_email +14 -0
  49. data/test/fixtures/raw_email10 +20 -0
  50. data/test/fixtures/raw_email12 +32 -0
  51. data/test/fixtures/raw_email13 +29 -0
  52. data/test/fixtures/raw_email2 +114 -0
  53. data/test/fixtures/raw_email3 +70 -0
  54. data/test/fixtures/raw_email4 +59 -0
  55. data/test/fixtures/raw_email5 +19 -0
  56. data/test/fixtures/raw_email6 +20 -0
  57. data/test/fixtures/raw_email7 +66 -0
  58. data/test/fixtures/raw_email8 +47 -0
  59. data/test/fixtures/raw_email9 +28 -0
  60. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  61. data/test/fixtures/raw_email_with_invalid_characters_in_content_type +104 -0
  62. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  63. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  64. data/test/fixtures/second_mailer/share.erb +1 -0
  65. data/test/fixtures/templates/signed_up.erb +3 -0
  66. data/test/fixtures/test_mailer/_subtemplate.text.erb +1 -0
  67. data/test/fixtures/test_mailer/custom_templating_extension.html.haml +6 -0
  68. data/test/fixtures/test_mailer/custom_templating_extension.text.haml +6 -0
  69. data/test/fixtures/test_mailer/implicitly_multipart_example.html.erb +10 -0
  70. data/test/fixtures/test_mailer/implicitly_multipart_example.html.erb~ +10 -0
  71. data/test/fixtures/test_mailer/implicitly_multipart_example.ignored.erb +1 -0
  72. data/test/fixtures/test_mailer/implicitly_multipart_example.rhtml.bak +1 -0
  73. data/test/fixtures/test_mailer/implicitly_multipart_example.text.erb +2 -0
  74. data/test/fixtures/test_mailer/implicitly_multipart_example.yaml.erb +1 -0
  75. data/test/fixtures/test_mailer/included_subtemplate.text.erb +1 -0
  76. data/test/fixtures/test_mailer/multipart_alternative.html.erb +1 -0
  77. data/test/fixtures/test_mailer/multipart_alternative.plain.erb +1 -0
  78. data/test/fixtures/test_mailer/rxml_template.rxml +2 -0
  79. data/test/fixtures/test_mailer/signed_up.html.erb +3 -0
  80. data/test/fixtures/url_test_mailer/signed_up_with_url.erb +5 -0
  81. data/test/mail_layout_test.rb +126 -0
  82. data/test/mail_render_test.rb +162 -0
  83. data/test/mail_service_test.rb +1214 -0
  84. metadata +308 -0
@@ -0,0 +1,29 @@
1
+ # LegacyMailers
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'legacy_mailers'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install legacy_mailers
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1,28 @@
1
+ module ActionMailer
2
+ module AdvAttrAccessor #:nodoc:
3
+ def adv_attr_accessor(name, deprecation=nil)
4
+ ivar = "@#{name}"
5
+ deprecation ||= "Please pass :#{name} as hash key to mail() instead"
6
+
7
+ class_eval <<-ACCESSORS, __FILE__, __LINE__ + 1
8
+ def #{name}=(value)
9
+ ActiveSupport::Deprecation.warn "#{name}= is deprecated. #{deprecation}"
10
+ #{ivar} = value
11
+ end
12
+
13
+ def #{name}(*args)
14
+ raise ArgumentError, "expected 0 or 1 parameters" unless args.length <= 1
15
+ if args.empty?
16
+ ActiveSupport::Deprecation.warn "#{name}() is deprecated and will be removed in future versions."
17
+ #{ivar} if instance_variable_names.include?(#{ivar.inspect})
18
+ else
19
+ ActiveSupport::Deprecation.warn "#{name}(value) is deprecated. #{deprecation}"
20
+ #{ivar} = args.first
21
+ end
22
+ end
23
+ ACCESSORS
24
+
25
+ self.protected_instance_variables << ivar if self.respond_to?(:protected_instance_variables)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,152 @@
1
+ require 'active_support/core_ext/object/try'
2
+
3
+ module ActionMailer
4
+ # This is the API which is deprecated and is going to be removed on Rails 3.1 release.
5
+ # Part of the old API will be deprecated after 3.1, for a smoother deprecation process.
6
+ # Check those in OldApi instead.
7
+ module DeprecatedApi #:nodoc:
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ [:charset, :content_type, :mime_version, :implicit_parts_order].each do |method|
12
+ class_eval <<-FILE, __FILE__, __LINE__ + 1
13
+ def self.default_#{method}
14
+ @@default_#{method}
15
+ end
16
+
17
+ def self.default_#{method}=(value)
18
+ ActiveSupport::Deprecation.warn "ActionMailer::Base.default_#{method}=value is deprecated, " <<
19
+ "use default :#{method} => value instead"
20
+ @@default_#{method} = value
21
+ end
22
+
23
+ @@default_#{method} = nil
24
+ FILE
25
+ end
26
+
27
+ class << self
28
+ alias_method_chain :respond_to?, :deprecated_api
29
+ alias_method_chain :method_missing, :deprecated_api
30
+ end
31
+ end
32
+
33
+ module ClassMethods
34
+ # Deliver the given mail object directly. This can be used to deliver
35
+ # a preconstructed mail object, like:
36
+ #
37
+ # email = MyMailer.create_some_mail(parameters)
38
+ # email.set_some_obscure_header "frobnicate"
39
+ # MyMailer.deliver(email)
40
+ def deliver(mail, show_warning=true)
41
+ if show_warning
42
+ ActiveSupport::Deprecation.warn "#{self}.deliver is deprecated, call " <<
43
+ "deliver in the mailer instance instead", caller[0,2]
44
+ end
45
+
46
+ raise "no mail object available for delivery!" unless mail
47
+ wrap_delivery_behavior(mail)
48
+ mail.deliver
49
+ mail
50
+ end
51
+
52
+ def template_root
53
+ self.view_paths && self.view_paths.first
54
+ end
55
+
56
+ def template_root=(root)
57
+ ActiveSupport::Deprecation.warn "template_root= is deprecated, use prepend_view_path instead", caller[0,2]
58
+ self.view_paths = ActionView::Base.process_view_paths(root)
59
+ end
60
+
61
+ def respond_to_with_deprecated_api?(method_symbol, include_private = false)
62
+ matches_dynamic_method?(method_symbol) || respond_to_without_deprecated_api?(method_symbol, false)
63
+ end
64
+
65
+ def method_missing_with_deprecated_api(method_symbol, *parameters)
66
+ if match = matches_dynamic_method?(method_symbol)
67
+ case match[1]
68
+ when 'create'
69
+ ActiveSupport::Deprecation.warn "#{self}.create_#{match[2]} is deprecated, " <<
70
+ "use #{self}.#{match[2]} instead", caller[0,2]
71
+ new(match[2], *parameters).message
72
+ when 'deliver'
73
+ ActiveSupport::Deprecation.warn "#{self}.deliver_#{match[2]} is deprecated, " <<
74
+ "use #{self}.#{match[2]}.deliver instead", caller[0,2]
75
+ new(match[2], *parameters).message.deliver
76
+ else method_missing_without_deprecated_api(method_symbol, *parameters)
77
+ end
78
+ else
79
+ method_missing_without_deprecated_api(method_symbol, *parameters)
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def matches_dynamic_method?(method_name)
86
+ method_name = method_name.to_s
87
+ /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name)
88
+ end
89
+ end
90
+
91
+ # Delivers a Mail object. By default, it delivers the cached mail
92
+ # object (from the <tt>create!</tt> method). If no cached mail object exists, and
93
+ # no alternate has been given as the parameter, this will fail.
94
+ def deliver!(mail = @_message)
95
+ ActiveSupport::Deprecation.warn "Calling deliver in the AM::Base object is deprecated, " <<
96
+ "please call deliver in the Mail instance", caller[0,2]
97
+ self.class.deliver(mail, false)
98
+ end
99
+ alias :deliver :deliver!
100
+
101
+ def render(*args)
102
+ options = args.last.is_a?(Hash) ? args.last : {}
103
+
104
+ if file = options[:file] and !file.index("/")
105
+ ActiveSupport::Deprecation.warn("render :file is deprecated except for absolute paths. " \
106
+ "Please use render :template instead")
107
+ options[:prefix] = _prefix
108
+ end
109
+
110
+ if options[:body].is_a?(Hash)
111
+ ActiveSupport::Deprecation.warn(':body in render deprecated. Please use instance ' <<
112
+ 'variables as assigns instead', caller[0,1])
113
+
114
+ options[:body].each { |k,v| instance_variable_set(:"@#{k}", v) }
115
+ end
116
+ super
117
+ end
118
+
119
+ # Render a message but does not set it as mail body. Useful for rendering
120
+ # data for part and attachments.
121
+ #
122
+ # Examples:
123
+ #
124
+ # render_message "special_message"
125
+ # render_message :template => "special_message"
126
+ # render_message :inline => "<%= 'Hi!' %>"
127
+ #
128
+ def render_message(*args)
129
+ ActiveSupport::Deprecation.warn "render_message is deprecated, use render instead", caller[0,2]
130
+ render(*args)
131
+ end
132
+
133
+ private
134
+
135
+ def initialize_defaults(*)
136
+ @charset ||= self.class.default_charset.try(:dup)
137
+ @content_type ||= self.class.default_content_type.try(:dup)
138
+ @implicit_parts_order ||= self.class.default_implicit_parts_order.try(:dup)
139
+ @mime_version ||= self.class.default_mime_version.try(:dup)
140
+ super
141
+ end
142
+
143
+ def create_parts
144
+ if @body.is_a?(Hash) && !@body.empty?
145
+ ActiveSupport::Deprecation.warn "Giving a hash to body is deprecated, please use instance variables instead", caller[0,2]
146
+ @body.each { |k, v| instance_variable_set(:"@#{k}", v) }
147
+ end
148
+ super
149
+ end
150
+
151
+ end
152
+ end
@@ -0,0 +1,258 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/object/try'
3
+ require 'active_support/core_ext/object/blank'
4
+
5
+ module ActionMailer
6
+ module OldApi #:nodoc:
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ extend ActionMailer::AdvAttrAccessor
11
+ self.protected_instance_variables.concat %w(@parts @mail_was_called @headers)
12
+
13
+ # Specify the BCC addresses for the message
14
+ adv_attr_accessor :bcc
15
+
16
+ # Specify the CC addresses for the message.
17
+ adv_attr_accessor :cc
18
+
19
+ # Specify the charset to use for the message. This defaults to the
20
+ # +default_charset+ specified for ActionMailer::Base.
21
+ adv_attr_accessor :charset
22
+
23
+ # Specify the content type for the message. This defaults to <tt>text/plain</tt>
24
+ # in most cases, but can be automatically set in some situations.
25
+ adv_attr_accessor :content_type
26
+
27
+ # Specify the from address for the message.
28
+ adv_attr_accessor :from
29
+
30
+ # Specify the address (if different than the "from" address) to direct
31
+ # replies to this message.
32
+ adv_attr_accessor :reply_to
33
+
34
+ # Specify the order in which parts should be sorted, based on content-type.
35
+ # This defaults to the value for the +default_implicit_parts_order+.
36
+ adv_attr_accessor :implicit_parts_order
37
+
38
+ # Defaults to "1.0", but may be explicitly given if needed.
39
+ adv_attr_accessor :mime_version
40
+
41
+ # The recipient addresses for the message, either as a string (for a single
42
+ # address) or an array (for multiple addresses).
43
+ adv_attr_accessor :recipients, "Please pass :to as hash key to mail() instead"
44
+
45
+ # The date on which the message was sent. If not set (the default), the
46
+ # header will be set by the delivery agent.
47
+ adv_attr_accessor :sent_on, "Please pass :date as hash key to mail() instead"
48
+
49
+ # Specify the subject of the message.
50
+ adv_attr_accessor :subject
51
+
52
+ # Specify the template name to use for current message. This is the "base"
53
+ # template name, without the extension or directory, and may be used to
54
+ # have multiple mailer methods share the same template.
55
+ adv_attr_accessor :template, "Please pass :template_name or :template_path as hash key to mail() instead"
56
+
57
+ # Define the body of the message. This is either a Hash (in which case it
58
+ # specifies the variables to pass to the template when it is rendered),
59
+ # or a string, in which case it specifies the actual text of the message.
60
+ adv_attr_accessor :body
61
+
62
+ def process(method_name, *args)
63
+ lookup_context.skip_default_locale!
64
+
65
+ initialize_defaults(method_name)
66
+ super
67
+
68
+ unless @mail_was_called
69
+ create_parts
70
+ create_mail
71
+ end
72
+ @_message
73
+ end
74
+ end
75
+
76
+ # Add a part to a multipart message, with the given content-type. The
77
+ # part itself is yielded to the block so that other properties (charset,
78
+ # body, headers, etc.) can be set on it.
79
+ def part(params)
80
+ ActiveSupport::Deprecation.warn "part() is deprecated and will be removed in future versions. " <<
81
+ "Please pass a block to mail() instead."
82
+ params = {:content_type => params} if String === params
83
+
84
+ if custom_headers = params.delete(:headers)
85
+ params.merge!(custom_headers)
86
+ end
87
+
88
+ part = Mail::Part.new(params)
89
+
90
+ yield part if block_given?
91
+ @parts << part
92
+ end
93
+
94
+ # Add an attachment to a multipart message. This is simply a part with the
95
+ # content-disposition set to "attachment".
96
+ def attachment(params, &block)
97
+ ActiveSupport::Deprecation.warn "attachment() is deprecated and will be removed in future versions. " <<
98
+ "Please use the attachments[] API instead."
99
+ params = { :content_type => params } if String === params
100
+
101
+ params[:content] ||= params.delete(:data) || params.delete(:body)
102
+
103
+ if params[:filename]
104
+ params = normalize_file_hash(params)
105
+ else
106
+ params = normalize_nonfile_hash(params)
107
+ end
108
+
109
+ part(params, &block)
110
+ end
111
+
112
+ protected
113
+
114
+ def normalize_nonfile_hash(params)
115
+ content_disposition = "attachment;"
116
+
117
+ mime_type = params.delete(:mime_type)
118
+
119
+ if content_type = params.delete(:content_type)
120
+ content_type = "#{mime_type || content_type};"
121
+ end
122
+
123
+ params[:body] = params.delete(:data) if params[:data]
124
+
125
+ { :content_type => content_type,
126
+ :content_disposition => content_disposition }.merge(params)
127
+ end
128
+
129
+ def normalize_file_hash(params)
130
+ filename = File.basename(params.delete(:filename))
131
+ content_disposition = "attachment; filename=\"#{File.basename(filename)}\""
132
+
133
+ mime_type = params.delete(:mime_type)
134
+
135
+ if (content_type = params.delete(:content_type)) && (content_type !~ /filename=/)
136
+ content_type = "#{mime_type || content_type}; filename=\"#{filename}\""
137
+ end
138
+
139
+ params[:body] = params.delete(:data) if params[:data]
140
+
141
+ { :content_type => content_type,
142
+ :content_disposition => content_disposition }.merge(params)
143
+ end
144
+
145
+ def create_mail
146
+ m = @_message
147
+
148
+ set_fields!({:subject => @subject, :to => @recipients, :from => @from,
149
+ :bcc => @bcc, :cc => @cc, :reply_to => @reply_to}, @charset)
150
+
151
+ m.mime_version = @mime_version if @mime_version
152
+ m.date = @sent_on.to_time rescue @sent_on if @sent_on
153
+
154
+ @headers.each { |k, v| m[k] = v }
155
+
156
+ real_content_type, ctype_attrs = parse_content_type
157
+ main_type, sub_type = split_content_type(real_content_type)
158
+
159
+ if @parts.size == 1 && @parts.first.parts.empty?
160
+ m.content_type([main_type, sub_type, ctype_attrs])
161
+ m.body = @parts.first.body.encoded
162
+ else
163
+ @parts.each do |p|
164
+ m.add_part(p)
165
+ end
166
+
167
+ m.body.set_sort_order(@implicit_parts_order)
168
+ m.body.sort_parts!
169
+
170
+ if real_content_type =~ /multipart/
171
+ ctype_attrs.delete "charset"
172
+ m.content_type([main_type, sub_type, ctype_attrs])
173
+ end
174
+ end
175
+
176
+ wrap_delivery_behavior!
177
+ m.content_transfer_encoding = '8bit' unless m.body.only_us_ascii?
178
+
179
+ @_message
180
+ end
181
+
182
+ # Set up the default values for the various instance variables of this
183
+ # mailer. Subclasses may override this method to provide different
184
+ # defaults.
185
+ def initialize_defaults(method_name)
186
+ @charset ||= self.class.default[:charset].try(:dup)
187
+ @content_type ||= self.class.default[:content_type].try(:dup)
188
+ @implicit_parts_order ||= self.class.default[:parts_order].try(:dup)
189
+ @mime_version ||= self.class.default[:mime_version].try(:dup)
190
+
191
+ @cc, @bcc, @reply_to, @subject, @from, @recipients = nil, nil, nil, nil, nil, nil
192
+
193
+ @mailer_name ||= self.class.mailer_name.dup
194
+ @template ||= method_name
195
+ @mail_was_called = false
196
+
197
+ @parts ||= []
198
+ @headers ||= {}
199
+ @sent_on ||= Time.now
200
+ @body ||= {}
201
+ end
202
+
203
+ def create_parts
204
+ if String === @body
205
+ @parts.unshift create_inline_part(@body)
206
+ elsif @parts.empty? || @parts.all? { |p| p.content_disposition =~ /^attachment/ }
207
+ lookup_context.find_all(@template, [@mailer_name]).each do |template|
208
+ self.formats = template.formats
209
+ @parts << create_inline_part(render(:template => template), template.mime_type)
210
+ end
211
+
212
+ if @parts.size > 1
213
+ @content_type = "multipart/alternative" if @content_type !~ /^multipart/
214
+ end
215
+
216
+ # If this is a multipart e-mail add the mime_version if it is not
217
+ # already set.
218
+ @mime_version ||= "1.0" unless @parts.empty?
219
+ end
220
+ end
221
+
222
+ def create_inline_part(body, mime_type=nil)
223
+ ct = mime_type || "text/plain"
224
+ main_type, sub_type = split_content_type(ct.to_s)
225
+
226
+ Mail::Part.new(
227
+ :content_type => [main_type, sub_type, {:charset => charset}],
228
+ :content_disposition => "inline",
229
+ :body => body
230
+ )
231
+ end
232
+
233
+ def set_fields!(headers, charset) #:nodoc:
234
+ m = @_message
235
+ m.charset = charset
236
+ m.subject ||= headers.delete(:subject) if headers[:subject]
237
+ m.to ||= headers.delete(:to) if headers[:to]
238
+ m.from ||= headers.delete(:from) if headers[:from]
239
+ m.cc ||= headers.delete(:cc) if headers[:cc]
240
+ m.bcc ||= headers.delete(:bcc) if headers[:bcc]
241
+ m.reply_to ||= headers.delete(:reply_to) if headers[:reply_to]
242
+ end
243
+
244
+ def split_content_type(ct)
245
+ ct.to_s.split("/")
246
+ end
247
+
248
+ def parse_content_type
249
+ if @content_type.blank?
250
+ [ nil, {} ]
251
+ else
252
+ ctype, *attrs = @content_type.split(/;\s*/)
253
+ attrs = Hash[attrs.map { |attr| attr.split(/=/, 2) }]
254
+ [ctype, {"charset" => @charset}.merge!(attrs)]
255
+ end
256
+ end
257
+ end
258
+ end