erector-rails4 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -1
  3. data/.travis.yml +1 -1
  4. data/Appraisals +4 -1
  5. data/Guardfile +23 -0
  6. data/README.md +6 -1
  7. data/erector-rails4.gemspec +10 -3
  8. data/gemfiles/rails_3.gemfile +1 -0
  9. data/gemfiles/rails_4.0.gemfile +1 -0
  10. data/gemfiles/rails_4.1.gemfile +2 -1
  11. data/lib/erector-rails4.rb +1 -0
  12. data/lib/erector.rb +1 -4
  13. data/lib/erector/abstract_widget.rb +1 -12
  14. data/lib/erector/after_initialize.rb +7 -7
  15. data/lib/erector/caching.rb +35 -71
  16. data/lib/erector/element.rb +4 -4
  17. data/lib/erector/externals.rb +0 -8
  18. data/lib/erector/html_widget.rb +0 -11
  19. data/lib/erector/needs.rb +20 -9
  20. data/lib/erector/output.rb +2 -4
  21. data/lib/erector/promise.rb +4 -4
  22. data/lib/erector/rails.rb +6 -1
  23. data/lib/erector/rails/autoload_monkeypatch.rb +1 -1
  24. data/lib/erector/rails/railtie.rb +15 -8
  25. data/lib/erector/rails/template_handler.rb +2 -1
  26. data/lib/erector/text.rb +4 -8
  27. data/lib/erector/version.rb +1 -1
  28. data/lib/erector/widget.rb +2 -11
  29. data/lib/erector/xml_widget.rb +14 -18
  30. data/script/bootstrap +30 -0
  31. data/script/cibuild +3 -0
  32. data/script/performance +3 -0
  33. data/script/release +38 -0
  34. data/spec/dummy/app/views/test/_partial_with_rails_helpers.rb +7 -0
  35. data/spec/dummy/app/views/test/_user.rb +7 -0
  36. data/spec/dummy/app/views/test/_virtual_path_partial.rb +5 -0
  37. data/spec/dummy/app/views/test/erb_from_erector.html.rb +1 -1
  38. data/spec/dummy/app/views/test/erector_with_helpers_from_erb.html.erb +1 -0
  39. data/spec/dummy/app/views/test/{needs.html.rb → needs.rb} +0 -0
  40. data/spec/dummy/app/views/test/{needs_subclass.html.rb → needs_subclass.rb} +0 -0
  41. data/spec/dummy/app/views/test/render_partial.html.rb +1 -1
  42. data/spec/dummy/app/views/test/render_virtual_path.rb +7 -0
  43. data/spec/dummy/app/views/test/users.rb +9 -0
  44. data/spec/dummy/app/views/test_caching/_foos.rb +7 -0
  45. data/spec/dummy/app/views/test_caching/_partial.rb +9 -0
  46. data/spec/dummy/app/views/test_caching/cache_helper.rb +9 -0
  47. data/spec/dummy/app/views/test_caching/cache_helper_with_explicit_dependencies.rb +7 -0
  48. data/spec/dummy/app/views/test_caching/cache_helper_with_implicit_dependencies.rb +7 -0
  49. data/spec/dummy/app/views/test_caching/cache_helper_with_partial.rb +10 -0
  50. data/spec/dummy/app/views/test_caching/cache_helper_with_skip_digest.rb +9 -0
  51. data/spec/dummy/app/views/test_caching/cacheable_widget_with_dynamic_keys.rb +13 -0
  52. data/spec/dummy/app/views/test_caching/cacheable_widget_with_needs.rb +11 -0
  53. data/spec/dummy/app/views/test_caching/cacheable_widget_with_needs_keys.rb +11 -0
  54. data/spec/dummy/app/views/test_caching/cacheable_widget_with_skip_digest.rb +9 -0
  55. data/spec/dummy/app/views/test_caching/cacheable_widget_with_static_keys.rb +9 -0
  56. data/spec/dummy/config/application.rb +1 -1
  57. data/spec/dummy/spec/autoload_spec.rb +2 -2
  58. data/spec/dummy/spec/caching_spec.rb +180 -0
  59. data/spec/dummy/spec/form_builder_spec.rb +1 -1
  60. data/spec/dummy/spec/rails_helpers_spec.rb +21 -13
  61. data/spec/dummy/spec/rails_widget_spec.rb +1 -1
  62. data/spec/dummy/spec/render_spec.rb +23 -42
  63. data/spec/erector/dependency_spec.rb +0 -1
  64. data/spec/erector/html_spec.rb +4 -4
  65. data/spec/erector/indentation_spec.rb +2 -2
  66. data/spec/erector/needs_spec.rb +33 -0
  67. data/spec/erector/output_spec.rb +0 -2
  68. data/spec/erector/promise_spec.rb +2 -2
  69. data/spec/erector/widget_spec.rb +2 -2
  70. data/spec/performance/allocation_spec.rb +19 -0
  71. data/spec/performance/ruby_prof_spec.rb +23 -0
  72. data/spec/performance/widget_to_html_spec.rb +11 -5
  73. data/spec/spec_helper.rb +1 -10
  74. data/spec/support/capturing_output.rb +8 -0
  75. metadata +116 -17
  76. data/lib/erector/cache.rb +0 -41
  77. data/lib/erector/raw_string.rb +0 -12
  78. data/spec/dummy/spec/rails_spec_helper.rb +0 -10
  79. data/spec/erector/cache_spec.rb +0 -133
  80. data/spec/erector/caching_spec.rb +0 -202
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a74438b8e1a5e68fb0630a400e6863d5eef429d3
4
- data.tar.gz: 5a3bb013b485b9afd29a816abccf442dfec8126a
3
+ metadata.gz: a9340d97584f99c33a1f240e455c4d99976abff9
4
+ data.tar.gz: 45cad63111621752efeaca71702964bcd709072b
5
5
  SHA512:
6
- metadata.gz: acc2083f696024b0ece8425d4591674dbaadc00d5cfca2e6ab61f7a06fd225c5390bb39694bdca32c70e34d24bf80eaeaf60b145339eb8166cdeadc0faebaf5d
7
- data.tar.gz: af17c1bb8dbd175f441b7537f7314e4c4de03abfc0c09b44c9422f2f14a264e8499c681062e878bfc18bdc205da678c252f1f51aa040e416bf74b50394a66b4b
6
+ metadata.gz: ea474c7bbae811af74994baae3bae443b02f15a68f04f5e5a72516a8506c51cb7004d41e2e86478155b8a94ee9f91e29b94f9db0bc5af14c7c044fa2847745a3
7
+ data.tar.gz: d476856b91c0c21ad0b313a89d7ba20c736fabc48df1dce0b43b81e6b2d5f5c5299b3344a8b8f24e162458be636489331099d3cab332a9bc64c3a623f1189e9d
@@ -1,2 +1,2 @@
1
1
  service_name: travis-ci
2
- repo_token: J1XUNT8uCGW7viYD01wooJ6PSlVc3Iyt2
2
+ repo_token: PLlg5sgYq8Bo0YEjewCfjarEnzBu8K9jm
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
  cache: bundler
3
- script: bundle exec rspec
3
+ script: script/cibuild
4
4
  notifications:
5
5
  email: false
6
6
 
data/Appraisals CHANGED
@@ -1,14 +1,17 @@
1
1
  appraise 'rails-3' do
2
+ gem 'activesupport', '3.2.7'
2
3
  gem 'rails', '3.2.7'
3
4
  gem 'simple_form', '2.1.1'
4
5
  end
5
6
 
6
7
  appraise 'rails-4.0' do
8
+ gem 'activesupport', '4.0.0'
7
9
  gem 'rails', '4.0.0'
8
10
  gem 'simple_form', '3.0.2'
9
11
  end
10
12
 
11
13
  appraise 'rails-4.1' do
12
- gem 'rails', '4.1.0'
14
+ gem 'activesupport', '4.1.6'
15
+ gem 'rails', '4.1.6'
13
16
  gem 'simple_form', '3.0.2'
14
17
  end
@@ -0,0 +1,23 @@
1
+ group :main do
2
+ guard :rspec,
3
+ all_on_start: false,
4
+ all_after_pass: false,
5
+ spec_paths: ['spec/dummy', 'spec/erector'],
6
+ cmd: 'bundle exec rspec',
7
+ failed_mode: :focus do
8
+
9
+ watch(%r{^spec/.+_spec\.rb$})
10
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+
12
+ end
13
+ end
14
+
15
+ group :perf do
16
+ guard :rspec,
17
+ all_on_start: false,
18
+ all_after_pass: false,
19
+ spec_paths: ['spec/performance'],
20
+ cmd: 'bundle exec rspec' do
21
+
22
+ end
23
+ end
data/README.md CHANGED
@@ -1,10 +1,15 @@
1
1
  Erector for Rails 4
2
2
  =====
3
3
 
4
- [![Gem Version](https://badge.fury.io/rb/erector-rails4.png)](http://badge.fury.io/rb/erector-rails4) [![Build Status](https://travis-ci.org/adamjacobbecker/erector-rails4.png?branch=master)](https://travis-ci.org/adamjacobbecker/erector-rails4) [![Coverage Status](https://coveralls.io/repos/adamjacobbecker/erector-rails4/badge.png)](https://coveralls.io/r/adamjacobbecker/erector-rails4) [![Code Climate](https://codeclimate.com/github/adamjacobbecker/erector-rails4.png)](https://codeclimate.com/github/adamjacobbecker/erector-rails4)
4
+ [![version]](http://badge.fury.io/rb/erector-rails4) [![status]](https://travis-ci.org/ajb/erector-rails4) [![coverage]](https://coveralls.io/r/ajb/erector-rails4) [![climate]](https://codeclimate.com/github/ajb/erector-rails4)
5
5
 
6
6
  This is a fork of [Erector](https://github.com/erector/erector) with a lotta old crap removed. Currently under active development, but in regular use at [DOBT](https://www.github.com/dobtco).
7
7
 
8
8
  ```ruby
9
9
  gem 'erector-rails4', require: 'erector'
10
10
  ```
11
+
12
+ [version]: http://img.shields.io/gem/v/erector-rails4.svg?style=flat
13
+ [status]: http://img.shields.io/travis/ajb/erector-rails4.svg?style=flat
14
+ [coverage]: http://img.shields.io/coveralls/ajb/erector-rails4.svg?style=flat
15
+ [climate]: http://img.shields.io/codeclimate/github/ajb/erector-rails4.svg?style=flat
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.name = "erector-rails4"
9
9
  s.version = Erector::VERSION
10
10
 
11
- s.required_ruby_version = Gem::Requirement.new('>= 2.0.0')
11
+ s.required_ruby_version = Gem::Requirement.new('>= 1.9.3')
12
12
  s.authors = ["Alex Chaffee", "Brian Takita", "Jeff Dean", "Jim Kingdon", "John Firebaugh", "Adam Becker"]
13
13
  s.summary = "Erector, for Rails 4"
14
14
  s.description = "This is a fork of Erector, updated for Rails 4."
@@ -18,16 +18,18 @@ Gem::Specification.new do |s|
18
18
  s.files = `git ls-files`.split("\n")
19
19
  s.test_files = `git ls-files -- {features,spec}/*`.split("\n")
20
20
 
21
- s.homepage = "http://github.com/adamjacobbecker/erector-rails4"
21
+ s.homepage = "http://github.com/ajb/erector-rails4"
22
22
  s.require_paths = ["lib"]
23
23
 
24
- s.add_dependency 'rails', '>= 3.0.0'
24
+ s.add_dependency 'activesupport', '>= 3.0.0'
25
25
  s.add_dependency 'treetop'
26
26
 
27
27
  s.add_development_dependency 'appraisal', '1.0.0'
28
28
  s.add_development_dependency 'coveralls', '0.7.0'
29
29
  s.add_development_dependency 'haml', '4.0.5'
30
+ s.add_development_dependency 'guard-rspec', '4.3.1'
30
31
  s.add_development_dependency 'nokogiri', '1.6.1'
32
+ s.add_development_dependency 'rails', '>= 3.0.0'
31
33
  s.add_development_dependency 'rr', '1.1.2'
32
34
  s.add_development_dependency 'rspec-rails', '2.14.2'
33
35
  s.add_development_dependency 'sass', '3.3.4'
@@ -35,4 +37,9 @@ Gem::Specification.new do |s|
35
37
  s.add_development_dependency 'sqlite3', '1.3.9'
36
38
  s.add_development_dependency 'wrong', '0.7.1'
37
39
 
40
+ if RUBY_VERSION >= '2.1'
41
+ s.add_development_dependency 'allocation_stats', '0.1.5'
42
+ s.add_development_dependency 'ruby-prof', '0.15.1'
43
+ end
44
+
38
45
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "activesupport", "3.2.7"
5
6
  gem "rails", "3.2.7"
6
7
  gem "simple_form", "2.1.1"
7
8
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "activesupport", "4.0.0"
5
6
  gem "rails", "4.0.0"
6
7
  gem "simple_form", "3.0.2"
7
8
 
@@ -2,7 +2,8 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "4.1.0"
5
+ gem "activesupport", "4.1.6"
6
+ gem "rails", "4.1.6"
6
7
  gem "simple_form", "3.0.2"
7
8
 
8
9
  gemspec :path => "../"
@@ -0,0 +1 @@
1
+ require 'erector'
@@ -1,15 +1,12 @@
1
1
  module Erector
2
2
  end
3
3
 
4
- require "cgi"
4
+ require "active_support/core_ext/string"
5
5
 
6
- require "erector/raw_string"
7
6
  require "erector/dependencies"
8
7
  require "erector/dependency"
9
8
  require "erector/externals"
10
9
  require "erector/output"
11
- require "erector/cache"
12
- require "erector/caching"
13
10
  require "erector/after_initialize"
14
11
  require "erector/needs"
15
12
  require "erector/html"
@@ -71,7 +71,7 @@ module Erector
71
71
  @_parent = eval("self", block.binding) if block
72
72
  @_block = block
73
73
 
74
- self.class.after_initialize(self)
74
+ self.class.call_after_initialize(self)
75
75
  end
76
76
 
77
77
  # Entry point for rendering a widget (and all its children). This method
@@ -101,16 +101,6 @@ module Erector
101
101
  _emit(options).to_s
102
102
  end
103
103
 
104
- # alias for #emit
105
- # @deprecated Please use {#emit} instead
106
- def to_s(*args)
107
- unless defined? @@already_warned_to_s
108
- $stderr.puts "Erector::Widget#to_s is deprecated. Please use #to_html instead. Called from #{caller.first}"
109
- @@already_warned_to_s = true
110
- end
111
- to_html(*args)
112
- end
113
-
114
104
  # Entry point for rendering a widget (and all its children). Same as
115
105
  # #render / #to_html only it returns an array, for theoretical performance
116
106
  # improvements when using a Rack server (like Sinatra or Rails Metal).
@@ -183,7 +173,6 @@ module Erector
183
173
  ensure
184
174
  @_output = original
185
175
  end
186
- alias_method :capture, :capture_content
187
176
 
188
177
  protected
189
178
  # executes this widget's #content method, which emits stuff onto the
@@ -5,18 +5,18 @@ module Erector
5
5
  end
6
6
 
7
7
  module ClassMethods
8
- def after_initialize(instance=nil, &blk)
9
- if blk
10
- after_initialize_parts << blk
11
- elsif instance
8
+ def after_initialize(&blk)
9
+ after_initialize_parts << blk
10
+ end
11
+
12
+ def call_after_initialize(instance)
13
+ if instance
12
14
  if superclass.respond_to?(:after_initialize)
13
- superclass.after_initialize instance
15
+ superclass.call_after_initialize instance
14
16
  end
15
17
  after_initialize_parts.each do |part|
16
18
  instance.instance_eval &part
17
19
  end
18
- else
19
- raise ArgumentError, "You must provide either an instance or a block"
20
20
  end
21
21
  end
22
22
 
@@ -5,93 +5,57 @@ module Erector
5
5
  end
6
6
 
7
7
  module ClassMethods
8
- def cacheable(value = true, opts = {})
9
- @cachable, @cache_opts = value, opts
8
+ def cacheable(*args)
9
+ options = args.extract_options!
10
10
 
11
- if value && value != true
12
- @cache_version = value
13
- end
14
- end
15
-
16
- alias_method :cachable, :cacheable
17
-
18
- def cachable?
19
- if @cachable.nil?
20
- superclass.respond_to?(:cachable?) && superclass.cachable?
21
- else
22
- @cachable
23
- end
24
- end
25
-
26
- def cache_opts
27
- if cachable?
28
- @cache_opts || {}
29
- end
30
- end
31
-
32
- def cache_version
33
- @cache_version || nil
11
+ @cacheable_opts = {
12
+ static_keys: args,
13
+ dynamic_keys: if options[:needs_keys]
14
+ needed_variables & options[:needs_keys]
15
+ else
16
+ needed_variables
17
+ end,
18
+ skip_digest: options[:skip_digest]
19
+ }
34
20
  end
35
21
 
36
- def cache
37
- Erector::Cache.instance
22
+ def cacheable_opts
23
+ @cacheable_opts
38
24
  end
39
25
  end
40
26
 
41
- def cache
42
- self.class.cache
27
+ def cacheable?
28
+ !self.class.cacheable_opts.nil?
43
29
  end
44
30
 
45
- def should_cache?
46
- if block.nil? && self.class.cachable? && caching_configured?
47
- true
48
- else
49
- false
50
- end
51
- end
31
+ def cache_name
32
+ [].tap do |a|
33
+ self.class.cacheable_opts[:static_keys].each do |x|
34
+ if x.is_a?(Symbol) && respond_to?(x)
35
+ a << send(x)
36
+ else
37
+ a << x
38
+ end
39
+ end
52
40
 
53
- def caching_configured?
54
- return true if !defined?(Rails)
55
- ::Rails.configuration.action_controller.perform_caching &&
56
- ::Rails.configuration.action_controller.cache_store
41
+ self.class.cacheable_opts[:dynamic_keys].each do |x|
42
+ a << instance_variable_get(:"@#{x}")
43
+ end
44
+ end.reject(&:nil?)
57
45
  end
58
46
 
59
- def cache_key_assigns
60
- if self.class.cache_opts[:only_keys]
61
- assigns.slice(*self.class.cache_opts[:only_keys])
62
- else
63
- assigns
64
- end
47
+ def cache_options
48
+ {
49
+ skip_digest: self.class.cacheable_opts[:skip_digest]
50
+ }
65
51
  end
66
52
 
67
53
  protected
68
54
  def _emit(options = {})
69
- if should_cache?
70
- if options[:output]
71
- # todo: document that either :buffer or :output can be used to specify an output buffer, and deprecate :output
72
- if options[:output].is_a? Output
73
- @_output = options[:output]
74
- else
75
- @_output = Output.new({:buffer => options[:output]}.merge(options))
76
- end
77
- else
78
- @_output = Output.new(options)
55
+ if cacheable? && options[:helpers].try(:respond_to?, :cache)
56
+ options[:helpers].cache cache_name, cache_options do
57
+ super
79
58
  end
80
-
81
- if (cached_str = cache[self.class, self.class.cache_version, cache_key_assigns, options[:content_method_name]])
82
- output << cached_str
83
- else
84
- cache[self.class, self.class.cache_version, cache_key_assigns, options[:content_method_name]] = super
85
- end
86
- else
87
- super
88
- end
89
- end
90
-
91
- def _emit_via(parent, options = {})
92
- if should_cache?
93
- parent.output << cache[self.class, self.class.cache_version, cache_key_assigns, options[:content_method_name]] ||= parent.capture_content { super }
94
- parent.output.widgets << self.class # todo: test!!!
95
59
  else
96
60
  super
97
61
  end
@@ -86,7 +86,7 @@ module Erector
86
86
 
87
87
  attributes ||= {}
88
88
  promise = if !value.nil?
89
- Promise.new(output, tag_name, attributes, false, newliney?(tag_name)) do
89
+ Promise.new(output, tag_name, attributes, false, self.class.newliney?(tag_name)) do
90
90
  if value.is_a? AbstractWidget
91
91
  widget value
92
92
  else
@@ -94,16 +94,16 @@ module Erector
94
94
  end
95
95
  end
96
96
  elsif block
97
- Promise.new(output, tag_name, attributes, false, newliney?(tag_name), &block)
97
+ Promise.new(output, tag_name, attributes, false, self.class.newliney?(tag_name), &block)
98
98
  else
99
- Promise.new(output, tag_name, attributes, false, newliney?(tag_name))
99
+ Promise.new(output, tag_name, attributes, false, self.class.newliney?(tag_name))
100
100
  end
101
101
  promise._render
102
102
  promise
103
103
  end
104
104
 
105
105
  def _empty_element(tag_name, attributes={})
106
- promise = Promise.new(output, tag_name, attributes, true, newliney?(tag_name))
106
+ promise = Promise.new(output, tag_name, attributes, true, self.class.newliney?(tag_name))
107
107
  promise._render
108
108
  promise
109
109
  end
@@ -30,14 +30,6 @@ module Erector
30
30
  my_dependencies.push(x)
31
31
  end
32
32
 
33
- # deprecated in favor of #depends_on
34
- # todo: warning
35
- def external(type, value, options = {})
36
- type = type.to_sym
37
- x = Dependency.new(type, value, options)
38
- my_dependencies << x unless my_dependencies.include?(x)
39
- end
40
-
41
33
  # returns all dependencies of the given type from this class and all its
42
34
  # superclasses
43
35
  def dependencies(type)
@@ -45,7 +45,6 @@ module Erector
45
45
  # * Attributes
46
46
  # * Text
47
47
  # * Needs
48
- # * Caching
49
48
  # * Externals
50
49
  # * AfterInitialize
51
50
  #
@@ -200,16 +199,6 @@ module Erector
200
199
  _render(options).to_s
201
200
  end
202
201
 
203
- # alias for #to_html
204
- # @deprecated Please use {#to_html} instead
205
- def to_s(*args)
206
- unless defined? @@already_warned_to_s
207
- $stderr.puts "Erector::Widget#to_s is deprecated. Please use #to_html instead. Called from #{caller.first}"
208
- @@already_warned_to_s = true
209
- end
210
- to_html(*args)
211
- end
212
-
213
202
  end
214
203
 
215
204
  public
@@ -36,28 +36,39 @@ module Erector
36
36
  # FancyForm.new(:name => 'Login')
37
37
  # will fail.
38
38
  #
39
+ def inherited(subclass)
40
+ subclass.needs(*self.get_needs)
41
+ end
42
+
39
43
  def needs(*args)
44
+ @needs ||= []
45
+
40
46
  args.each do |arg|
41
- (@needs ||= []) << (arg.nil? ? nil : (arg.is_a? Hash) ? arg : arg.to_sym)
47
+ @needs.push(if arg.nil?
48
+ nil
49
+ elsif arg.is_a? Hash
50
+ arg
51
+ elsif arg.is_a? Symbol
52
+ arg
53
+ else
54
+ fail 'arguments passed to :needs must be Nil, a Hash, or a Symbol'
55
+ end)
42
56
  end
43
57
  end
44
58
 
45
59
  def get_needs
46
- @needs ||= []
47
-
48
- ancestors[1..-1].inject(@needs.dup) do |needs, ancestor|
49
- needs.push(*ancestor.get_needs) if ancestor.respond_to?(:get_needs)
50
- needs
51
- end
60
+ @needs || []
52
61
  end
53
62
 
54
63
  def needed_variables
55
- @needed_variables ||= get_needs.map{|need| need.is_a?(Hash) ? need.keys : need}.flatten
64
+ @needed_variables ||= get_needs.map { |need|
65
+ need.is_a?(Hash) ? need.keys : need
66
+ }.flatten
56
67
  end
57
68
 
58
69
  def needed_defaults
59
70
  @needed_defaults ||= get_needs.inject({}) do |defaults, need|
60
- defaults = need.merge(defaults) if need.is_a? Hash
71
+ defaults = defaults.merge(need) if need.is_a? Hash
61
72
  defaults
62
73
  end
63
74
  end