hanami-view 1.3.1 → 2.0.0.alpha3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/LICENSE +20 -0
  4. data/README.md +17 -835
  5. data/hanami-view.gemspec +26 -16
  6. data/lib/hanami/view/application_configuration.rb +77 -0
  7. data/lib/hanami/view/application_context.rb +35 -0
  8. data/lib/hanami/view/application_view.rb +89 -0
  9. data/lib/hanami/view/context.rb +97 -0
  10. data/lib/hanami/view/context_helpers/content_helpers.rb +26 -0
  11. data/lib/hanami/view/decorated_attributes.rb +82 -0
  12. data/lib/hanami/view/errors.rb +31 -53
  13. data/lib/hanami/view/exposure.rb +126 -0
  14. data/lib/hanami/view/exposures.rb +74 -0
  15. data/lib/hanami/view/part.rb +217 -0
  16. data/lib/hanami/view/part_builder.rb +140 -0
  17. data/lib/hanami/view/path.rb +68 -0
  18. data/lib/hanami/view/render_environment.rb +62 -0
  19. data/lib/hanami/view/render_environment_missing.rb +44 -0
  20. data/lib/hanami/view/rendered.rb +55 -0
  21. data/lib/hanami/view/renderer.rb +79 -0
  22. data/lib/hanami/view/scope.rb +189 -0
  23. data/lib/hanami/view/scope_builder.rb +98 -0
  24. data/lib/hanami/view/standalone_view.rb +400 -0
  25. data/lib/hanami/view/tilt/erb.rb +26 -0
  26. data/lib/hanami/view/tilt/erbse.rb +21 -0
  27. data/lib/hanami/view/tilt/haml.rb +26 -0
  28. data/lib/hanami/view/tilt.rb +78 -0
  29. data/lib/hanami/view/version.rb +5 -5
  30. data/lib/hanami/view.rb +208 -223
  31. data/lib/hanami-view.rb +3 -1
  32. metadata +120 -70
  33. data/LICENSE.md +0 -22
  34. data/lib/hanami/layout.rb +0 -190
  35. data/lib/hanami/presenter.rb +0 -98
  36. data/lib/hanami/view/configuration.rb +0 -504
  37. data/lib/hanami/view/dsl.rb +0 -347
  38. data/lib/hanami/view/escape.rb +0 -225
  39. data/lib/hanami/view/inheritable.rb +0 -54
  40. data/lib/hanami/view/rendering/layout_finder.rb +0 -128
  41. data/lib/hanami/view/rendering/layout_registry.rb +0 -69
  42. data/lib/hanami/view/rendering/layout_scope.rb +0 -274
  43. data/lib/hanami/view/rendering/null_layout.rb +0 -52
  44. data/lib/hanami/view/rendering/null_local.rb +0 -82
  45. data/lib/hanami/view/rendering/null_template.rb +0 -83
  46. data/lib/hanami/view/rendering/null_view.rb +0 -26
  47. data/lib/hanami/view/rendering/options.rb +0 -24
  48. data/lib/hanami/view/rendering/partial.rb +0 -31
  49. data/lib/hanami/view/rendering/partial_file.rb +0 -29
  50. data/lib/hanami/view/rendering/partial_finder.rb +0 -75
  51. data/lib/hanami/view/rendering/partial_templates_finder.rb +0 -73
  52. data/lib/hanami/view/rendering/registry.rb +0 -134
  53. data/lib/hanami/view/rendering/scope.rb +0 -108
  54. data/lib/hanami/view/rendering/subscope.rb +0 -56
  55. data/lib/hanami/view/rendering/template.rb +0 -69
  56. data/lib/hanami/view/rendering/template_finder.rb +0 -55
  57. data/lib/hanami/view/rendering/template_name.rb +0 -50
  58. data/lib/hanami/view/rendering/templates_finder.rb +0 -144
  59. data/lib/hanami/view/rendering/view_finder.rb +0 -37
  60. data/lib/hanami/view/rendering.rb +0 -294
  61. data/lib/hanami/view/template.rb +0 -57
metadata CHANGED
@@ -1,145 +1,194 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami-view
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 2.0.0.alpha3
5
5
  platform: ruby
6
6
  authors:
7
- - Luca Guidi
8
- autorequire:
7
+ - Tim Riley
8
+ - Piotr Solnica
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2019-01-18 00:00:00.000000000 Z
12
+ date: 2021-11-09 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: tilt
15
+ name: concurrent-ruby
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
18
  - - "~>"
18
19
  - !ruby/object:Gem::Version
19
- version: '2.0'
20
+ version: '1.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: dry-configurable
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '0.13'
20
35
  - - ">="
21
36
  - !ruby/object:Gem::Version
22
- version: 2.0.1
37
+ version: 0.13.0
23
38
  type: :runtime
24
39
  prerelease: false
25
40
  version_requirements: !ruby/object:Gem::Requirement
26
41
  requirements:
27
42
  - - "~>"
28
43
  - !ruby/object:Gem::Version
29
- version: '2.0'
44
+ version: '0.13'
30
45
  - - ">="
31
46
  - !ruby/object:Gem::Version
32
- version: 2.0.1
47
+ version: 0.13.0
33
48
  - !ruby/object:Gem::Dependency
34
- name: hanami-utils
49
+ name: dry-core
35
50
  requirement: !ruby/object:Gem::Requirement
36
51
  requirements:
37
52
  - - "~>"
38
53
  - !ruby/object:Gem::Version
39
- version: '1.3'
54
+ version: '0.5'
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0.5'
40
58
  type: :runtime
41
59
  prerelease: false
42
60
  version_requirements: !ruby/object:Gem::Requirement
43
61
  requirements:
44
62
  - - "~>"
45
63
  - !ruby/object:Gem::Version
46
- version: '1.3'
64
+ version: '0.5'
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0.5'
47
68
  - !ruby/object:Gem::Dependency
48
- name: bundler
69
+ name: dry-inflector
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.1'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.1'
82
+ - !ruby/object:Gem::Dependency
83
+ name: tilt
49
84
  requirement: !ruby/object:Gem::Requirement
50
85
  requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.0'
51
89
  - - ">="
52
90
  - !ruby/object:Gem::Version
53
- version: '1.6'
54
- - - "<"
91
+ version: 2.0.6
92
+ type: :runtime
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '2.0'
99
+ - - ">="
55
100
  - !ruby/object:Gem::Version
56
- version: '3'
101
+ version: 2.0.6
102
+ - !ruby/object:Gem::Dependency
103
+ name: bundler
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
57
109
  type: :development
58
110
  prerelease: false
59
111
  version_requirements: !ruby/object:Gem::Requirement
60
112
  requirements:
61
113
  - - ">="
62
114
  - !ruby/object:Gem::Version
63
- version: '1.6'
64
- - - "<"
65
- - !ruby/object:Gem::Version
66
- version: '3'
115
+ version: '0'
67
116
  - !ruby/object:Gem::Dependency
68
- name: rspec
117
+ name: rake
69
118
  requirement: !ruby/object:Gem::Requirement
70
119
  requirements:
71
- - - "~>"
120
+ - - ">="
72
121
  - !ruby/object:Gem::Version
73
- version: '3.7'
122
+ version: '0'
74
123
  type: :development
75
124
  prerelease: false
76
125
  version_requirements: !ruby/object:Gem::Requirement
77
126
  requirements:
78
- - - "~>"
127
+ - - ">="
79
128
  - !ruby/object:Gem::Version
80
- version: '3.7'
129
+ version: '0'
81
130
  - !ruby/object:Gem::Dependency
82
- name: rake
131
+ name: rspec
83
132
  requirement: !ruby/object:Gem::Requirement
84
133
  requirements:
85
- - - "~>"
134
+ - - ">="
86
135
  - !ruby/object:Gem::Version
87
- version: '12'
136
+ version: '0'
88
137
  type: :development
89
138
  prerelease: false
90
139
  version_requirements: !ruby/object:Gem::Requirement
91
140
  requirements:
92
- - - "~>"
141
+ - - ">="
93
142
  - !ruby/object:Gem::Version
94
- version: '12'
95
- description: View layer for Hanami
143
+ version: '0'
144
+ description: A complete, standalone view rendering system that gives you everything
145
+ you need to write well-factored view code
96
146
  email:
97
- - me@lucaguidi.com
147
+ - tim@icelab.com.au
148
+ - piotr.solnica@gmail.com
98
149
  executables: []
99
150
  extensions: []
100
151
  extra_rdoc_files: []
101
152
  files:
102
153
  - CHANGELOG.md
103
- - LICENSE.md
154
+ - LICENSE
104
155
  - README.md
105
156
  - hanami-view.gemspec
106
157
  - lib/hanami-view.rb
107
- - lib/hanami/layout.rb
108
- - lib/hanami/presenter.rb
109
158
  - lib/hanami/view.rb
110
- - lib/hanami/view/configuration.rb
111
- - lib/hanami/view/dsl.rb
159
+ - lib/hanami/view/application_configuration.rb
160
+ - lib/hanami/view/application_context.rb
161
+ - lib/hanami/view/application_view.rb
162
+ - lib/hanami/view/context.rb
163
+ - lib/hanami/view/context_helpers/content_helpers.rb
164
+ - lib/hanami/view/decorated_attributes.rb
112
165
  - lib/hanami/view/errors.rb
113
- - lib/hanami/view/escape.rb
114
- - lib/hanami/view/inheritable.rb
115
- - lib/hanami/view/rendering.rb
116
- - lib/hanami/view/rendering/layout_finder.rb
117
- - lib/hanami/view/rendering/layout_registry.rb
118
- - lib/hanami/view/rendering/layout_scope.rb
119
- - lib/hanami/view/rendering/null_layout.rb
120
- - lib/hanami/view/rendering/null_local.rb
121
- - lib/hanami/view/rendering/null_template.rb
122
- - lib/hanami/view/rendering/null_view.rb
123
- - lib/hanami/view/rendering/options.rb
124
- - lib/hanami/view/rendering/partial.rb
125
- - lib/hanami/view/rendering/partial_file.rb
126
- - lib/hanami/view/rendering/partial_finder.rb
127
- - lib/hanami/view/rendering/partial_templates_finder.rb
128
- - lib/hanami/view/rendering/registry.rb
129
- - lib/hanami/view/rendering/scope.rb
130
- - lib/hanami/view/rendering/subscope.rb
131
- - lib/hanami/view/rendering/template.rb
132
- - lib/hanami/view/rendering/template_finder.rb
133
- - lib/hanami/view/rendering/template_name.rb
134
- - lib/hanami/view/rendering/templates_finder.rb
135
- - lib/hanami/view/rendering/view_finder.rb
136
- - lib/hanami/view/template.rb
166
+ - lib/hanami/view/exposure.rb
167
+ - lib/hanami/view/exposures.rb
168
+ - lib/hanami/view/part.rb
169
+ - lib/hanami/view/part_builder.rb
170
+ - lib/hanami/view/path.rb
171
+ - lib/hanami/view/render_environment.rb
172
+ - lib/hanami/view/render_environment_missing.rb
173
+ - lib/hanami/view/rendered.rb
174
+ - lib/hanami/view/renderer.rb
175
+ - lib/hanami/view/scope.rb
176
+ - lib/hanami/view/scope_builder.rb
177
+ - lib/hanami/view/standalone_view.rb
178
+ - lib/hanami/view/tilt.rb
179
+ - lib/hanami/view/tilt/erb.rb
180
+ - lib/hanami/view/tilt/erbse.rb
181
+ - lib/hanami/view/tilt/haml.rb
137
182
  - lib/hanami/view/version.rb
138
- homepage: http://hanamirb.org
183
+ homepage: https://dry-rb.org/gems/hanami-view
139
184
  licenses:
140
185
  - MIT
141
- metadata: {}
142
- post_install_message:
186
+ metadata:
187
+ allowed_push_host: https://rubygems.org
188
+ changelog_uri: https://github.com/hanami/view/blob/main/CHANGELOG.md
189
+ source_code_uri: https://github.com/hanami/view
190
+ bug_tracker_uri: https://github.com/hanami/view/issues
191
+ post_install_message:
143
192
  rdoc_options: []
144
193
  require_paths:
145
194
  - lib
@@ -147,15 +196,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
147
196
  requirements:
148
197
  - - ">="
149
198
  - !ruby/object:Gem::Version
150
- version: 2.3.0
199
+ version: 2.4.0
151
200
  required_rubygems_version: !ruby/object:Gem::Requirement
152
201
  requirements:
153
- - - ">="
202
+ - - ">"
154
203
  - !ruby/object:Gem::Version
155
- version: '0'
204
+ version: 1.3.1
156
205
  requirements: []
157
- rubygems_version: 3.0.2
158
- signing_key:
206
+ rubygems_version: 3.2.3
207
+ signing_key:
159
208
  specification_version: 4
160
- summary: View layer for Hanami, with a separation between views and templates
209
+ summary: A complete, standalone view rendering system that gives you everything you
210
+ need to write well-factored view code
161
211
  test_files: []
data/LICENSE.md DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2014-2017 Luca Guidi
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/hanami/layout.rb DELETED
@@ -1,190 +0,0 @@
1
- require 'hanami/utils/class_attribute'
2
- require 'hanami/view/rendering/layout_registry'
3
- require 'hanami/view/rendering/layout_scope'
4
- require 'hanami/view/rendering/null_layout'
5
- require 'hanami/view/rendering/null_view'
6
-
7
- module Hanami
8
- # Layout
9
- #
10
- # @since 0.1.0
11
- #
12
- # @see Hanami::Layout::ClassMethods
13
- module Layout
14
- # Register a layout
15
- #
16
- # @api private
17
- # @since 0.1.0
18
- #
19
- # @example
20
- # require 'hanami/view'
21
- #
22
- # class ApplicationLayout
23
- # include Hanami::Layout
24
- # end
25
- def self.included(base)
26
- conf = Hanami::View::Configuration.for(base)
27
- conf.add_layout(base)
28
-
29
- base.class_eval do
30
- extend Hanami::View::Dsl.dup
31
- extend ClassMethods
32
-
33
- include Utils::ClassAttribute
34
- class_attribute :configuration
35
-
36
- self.configuration = conf.duplicate
37
- end
38
-
39
- conf.copy!(base)
40
- end
41
-
42
- # Class level API
43
- #
44
- # @since 0.1.0
45
- module ClassMethods
46
- # Template name suffix
47
- #
48
- # @api private
49
- # @since 0.1.0
50
- #
51
- # @see Hanami::Layout::ClassMethods#suffix
52
- # @see Hanami::Layout::ClassMethods#template
53
- SUFFIX = '_layout'.freeze
54
-
55
- # A registry that holds all the registered layouts.
56
- #
57
- # @api private
58
- # @since 0.1.0
59
- #
60
- # @see Hanami::View::Rendering::LayoutRegistry
61
- def registry
62
- @registry ||= View::Rendering::LayoutRegistry.new(self)
63
- end
64
-
65
- # Template name
66
- #
67
- # @api private
68
- # @since 0.1.0
69
- #
70
- # @see Hanami::Layout::ClassMethods#SUFFIX
71
- # @see Hanami::Layout::ClassMethods#suffix
72
- #
73
- # @example
74
- # # Given a template 'templates/application.html.erb'
75
- #
76
- # class ApplicationLayout
77
- # include Hanami::Layout
78
- # end
79
- #
80
- # ApplicationLayout.template # => 'application'
81
- def template
82
- super.sub(suffix, '')
83
- end
84
-
85
- # Template name suffix
86
- #
87
- # @api private
88
- # @since 0.1.0
89
- #
90
- # @see Hanami::Layout::ClassMethods#SUFFIX
91
- # @see Hanami::Layout::ClassMethods#template
92
- def suffix
93
- SUFFIX
94
- end
95
-
96
- protected
97
-
98
- # Loading mechanism hook.
99
- #
100
- # @api private
101
- # @since 0.1.0
102
- #
103
- # @see Hanami::View.load!
104
- def load!
105
- load_registry!
106
- configuration.freeze
107
- end
108
-
109
- private
110
-
111
- # @api private
112
- def load_registry!
113
- @registry = nil
114
- registry.freeze
115
- end
116
- end
117
-
118
- # Initialize a layout
119
- #
120
- # @param scope [Hanami::View::Rendering::Scope,::Hash] view rendering scope.
121
- # Optionally a scope can be expressed as a Ruby `::Hash`, but it MUST contain
122
- # the `:format` key, to specify which template to render.
123
- # @option scope [Symbol] :format the format to render (e.g. `:html`, `:xml`, `:json`)
124
- # This is mandatory only if a `:Hash` is passed as `scope`.
125
- #
126
- # @param rendered [String] the output of the view rendering process
127
- #
128
- # @api private
129
- # @since 0.1.0
130
- #
131
- # @see Hanami::View::Rendering#render
132
- def initialize(scope, rendered)
133
- # NOTE: This complex data transformation is due to a combination of a bug and the intent of maintaing backward compat (SemVer).
134
- # See https://github.com/hanami/view/pull/156
135
- s, r = *case scope
136
- when ::Hash
137
- [Hanami::View::Rendering::Scope.new(Hanami::View::Rendering::NullView, scope), rendered]
138
- when Hanami::View::Template
139
- [Hanami::View::Rendering::Scope.new(Hanami::View::Rendering::NullView, rendered.merge(format: scope.format)), ""]
140
- else
141
- [scope, rendered]
142
- end
143
-
144
- @scope = View::Rendering::LayoutScope.new(self, s)
145
- @rendered = r
146
- end
147
-
148
- # Render the layout
149
- #
150
- # @return [String] the output of the rendering process
151
- #
152
- # @api private
153
- # @since 0.1.0
154
- #
155
- # @see Hanami::View::Rendering#render
156
- def render
157
- template.render(@scope, &Proc.new{@rendered})
158
- end
159
-
160
- # It tries to invoke a method for the view or a local for the given key.
161
- # If the lookup fails, it returns a null object.
162
- #
163
- # @return [Object,Hanami::View::Rendering::NullLocal] the returning value
164
- #
165
- # @since 1.1.0
166
- #
167
- # @example Safe method navigation
168
- # class ApplicationLayout
169
- # include Hanami::Layout
170
- #
171
- # def render_flash
172
- # return if local(:flash).nil?
173
- #
174
- # # ...
175
- # end
176
- # end
177
- def local(key)
178
- @scope.local(key)
179
- end
180
-
181
- protected
182
- # The template for the current format
183
- #
184
- # @api private
185
- # @since 0.1.0
186
- def template
187
- self.class.registry.resolve({format: @scope.format})
188
- end
189
- end
190
- end
@@ -1,98 +0,0 @@
1
- require 'hanami/view/escape'
2
-
3
- module Hanami
4
- # Presenter pattern implementation
5
- #
6
- # It delegates to the wrapped object the missing method invocations.
7
- #
8
- # The output of concrete and delegated methods is escaped as XSS prevention.
9
- #
10
- # @since 0.1.0
11
- #
12
- # @example Basic usage
13
- # require 'hanami/view'
14
- #
15
- # class Map
16
- # attr_reader :locations
17
- #
18
- # def initialize(locations)
19
- # @locations = locations
20
- # end
21
- #
22
- # def location_names
23
- # @locations.join(', ')
24
- # end
25
- # end
26
- #
27
- # class MapPresenter
28
- # include Hanami::Presenter
29
- #
30
- # def count
31
- # locations.count
32
- # end
33
- #
34
- # def location_names
35
- # super.upcase
36
- # end
37
- #
38
- # def inspect_object
39
- # @object.inspect
40
- # end
41
- # end
42
- #
43
- # map = Map.new(['Rome', 'Boston'])
44
- # presenter = MapPresenter.new(map)
45
- #
46
- # # access a map method
47
- # puts presenter.locations # => ['Rome', 'Boston']
48
- #
49
- # # access presenter concrete methods
50
- # puts presenter.count # => 1
51
- #
52
- # # uses super to access original object implementation
53
- # puts presenter.location_names # => 'ROME, BOSTON'
54
- #
55
- # # it has private access to the original object
56
- # puts presenter.inspect_object # => #<Map:0x007fdeada0b2f0 @locations=["Rome", "Boston"]>
57
- #
58
- # @example Escape
59
- # require 'hanami/view'
60
- #
61
- # User = Struct.new(:first_name, :last_name)
62
- #
63
- # class UserPresenter
64
- # include Hanami::Presenter
65
- #
66
- # def full_name
67
- # [first_name, last_name].join(' ')
68
- # end
69
- #
70
- # def raw_first_name
71
- # _raw first_name
72
- # end
73
- # end
74
- #
75
- # first_name = '<script>alert('xss')</script>'
76
- #
77
- # user = User.new(first_name, nil)
78
- # presenter = UserPresenter.new(user)
79
- #
80
- # presenter.full_name
81
- # # => "&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;"
82
- #
83
- # presenter.raw_full_name
84
- # # => "<script>alert('xss')</script>"
85
- module Presenter
86
- # Inject escape logic into the given class.
87
- #
88
- # @since 0.4.0
89
- # @api private
90
- #
91
- # @see Hanami::View::Escape
92
- def self.included(base)
93
- base.class_eval do
94
- include ::Hanami::View::Escape::Presentable
95
- end
96
- end
97
- end
98
- end