dry-view 0.5.1 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +143 -18
- data/LICENSE +20 -0
- data/README.md +22 -14
- data/dry-view.gemspec +29 -21
- data/lib/dry-view.rb +3 -1
- data/lib/dry/view.rb +514 -2
- data/lib/dry/view/context.rb +80 -0
- data/lib/dry/view/decorated_attributes.rb +82 -0
- data/lib/dry/view/errors.rb +29 -0
- data/lib/dry/view/exposure.rb +35 -14
- data/lib/dry/view/exposures.rb +18 -6
- data/lib/dry/view/part.rb +166 -53
- data/lib/dry/view/part_builder.rb +140 -0
- data/lib/dry/view/path.rb +35 -7
- data/lib/dry/view/render_environment.rb +62 -0
- data/lib/dry/view/render_environment_missing.rb +44 -0
- data/lib/dry/view/rendered.rb +55 -0
- data/lib/dry/view/renderer.rb +36 -29
- data/lib/dry/view/scope.rb +160 -14
- data/lib/dry/view/scope_builder.rb +98 -0
- data/lib/dry/view/tilt.rb +78 -0
- data/lib/dry/view/tilt/erb.rb +26 -0
- data/lib/dry/view/tilt/erbse.rb +21 -0
- data/lib/dry/view/tilt/haml.rb +26 -0
- data/lib/dry/view/version.rb +5 -2
- metadata +78 -115
- data/.gitignore +0 -26
- data/.rspec +0 -2
- data/.travis.yml +0 -23
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -22
- data/LICENSE.md +0 -10
- data/Rakefile +0 -6
- data/benchmarks/templates/button.html.erb +0 -1
- data/benchmarks/view.rb +0 -24
- data/bin/console +0 -7
- data/lib/dry/view/controller.rb +0 -155
- data/lib/dry/view/decorator.rb +0 -45
- data/lib/dry/view/missing_renderer.rb +0 -15
- data/spec/fixtures/templates/_hello.html.slim +0 -1
- data/spec/fixtures/templates/decorated_parts.html.slim +0 -4
- data/spec/fixtures/templates/edit.html.slim +0 -11
- data/spec/fixtures/templates/empty.html.slim +0 -1
- data/spec/fixtures/templates/greeting.html.slim +0 -2
- data/spec/fixtures/templates/hello.html.slim +0 -1
- data/spec/fixtures/templates/layouts/app.html.slim +0 -6
- data/spec/fixtures/templates/layouts/app.txt.erb +0 -3
- data/spec/fixtures/templates/parts_with_args.html.slim +0 -3
- data/spec/fixtures/templates/parts_with_args/_box.html.slim +0 -3
- data/spec/fixtures/templates/shared/_index_table.html.slim +0 -2
- data/spec/fixtures/templates/shared/_shared_hello.html.slim +0 -1
- data/spec/fixtures/templates/tasks.html.slim +0 -3
- data/spec/fixtures/templates/user.html.slim +0 -2
- data/spec/fixtures/templates/users.html.slim +0 -5
- data/spec/fixtures/templates/users.txt.erb +0 -3
- data/spec/fixtures/templates/users/_row.html.slim +0 -2
- data/spec/fixtures/templates/users/_tbody.html.slim +0 -5
- data/spec/fixtures/templates/users_with_count.html.slim +0 -5
- data/spec/fixtures/templates/users_with_count_inherit.html.slim +0 -6
- data/spec/fixtures/templates_override/_hello.html.slim +0 -1
- data/spec/fixtures/templates_override/users.html.slim +0 -5
- data/spec/integration/decorator_spec.rb +0 -80
- data/spec/integration/exposures_spec.rb +0 -392
- data/spec/integration/part/decorated_attributes_spec.rb +0 -157
- data/spec/integration/view_spec.rb +0 -133
- data/spec/spec_helper.rb +0 -46
- data/spec/unit/controller_spec.rb +0 -37
- data/spec/unit/decorator_spec.rb +0 -61
- data/spec/unit/exposure_spec.rb +0 -227
- data/spec/unit/exposures_spec.rb +0 -103
- data/spec/unit/part_spec.rb +0 -90
- data/spec/unit/renderer_spec.rb +0 -57
- data/spec/unit/scope_spec.rb +0 -53
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e819c2e7fc0b6fe2aed059b2d63993b2e107e7d9b682652dc9a0dedefdf7458
|
|
4
|
+
data.tar.gz: e45c5c695005d30eb755ccffd22efc5bbdf7c91b018f4c2fe859cb6b015f70bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4797bca5d6dba8ba676794d9ff21c531c1ee303a0af87f924ee09e89c9faadc898f5b1dc9d24731c32e62d5b5c031efa4b6f8a7062ab30bd3f7ea4397eac7b2
|
|
7
|
+
data.tar.gz: 60fe4d2131720fd19cebe465283f06f05abb0247b094fc946ff96ac9b0b77c5962561ee68da1dd067876ace1d5def0caccdf18e7598a25800cf199aad5f36772
|
data/CHANGELOG.md
CHANGED
|
@@ -1,21 +1,129 @@
|
|
|
1
|
-
|
|
1
|
+
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
|
2
|
+
|
|
3
|
+
## 0.7.1 2021-02-08
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Fixed
|
|
7
|
+
|
|
8
|
+
- Template not found errors properly show configured paths (@adam12 in #144)
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Compatible with Ruby 2.7/3.0 keyword argument handling (@flash-gordon in 4e7cefb)
|
|
13
|
+
- dry-equalizer dependency dropped in favor of dry-core (@solnic)
|
|
14
|
+
|
|
15
|
+
[Compare v0.7.0...v0.7.1](https://github.com/dry-rb/dry-view/compare/v0.7.0...v0.7.1)
|
|
16
|
+
|
|
17
|
+
## 0.7.0 2019-03-06
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Raise a `Dry::View::UndefinedConfigError` when a view is called but no paths have been configured (timriley in [#130][pr130])
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Avoid a `SystemStackError` when a view is configured with a template that cannot be found on the filesystem (timriley in [#129][pr129])
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- [BREAKING] Move `Dry::View::Renderer::TemplateNotFoundError` to `Dry::View::TemplateNotFoundError` (timriley in [#130][pr130])
|
|
31
|
+
- [BREAKING] `Dry::View::UndefinedConfigError` is raised instead of `Dry::View::UndefinedTemplateError` when a view is called but no template has been configured (timriley in [#130][pr130])
|
|
32
|
+
- Stop searching upwards through parent directories when rendering a view's template (as opposed to partials) (timriley in [#130][pr130])
|
|
33
|
+
- Stop searching in `shared/` subdirectories when rendering a view's template (as opposed to partials) (timriley in [#130][pr130])
|
|
34
|
+
- Adjust template lookup cache keys to ensure no false hits (timriley in [#130][pr130])
|
|
35
|
+
|
|
36
|
+
[Compare v0.6.0...v0.7.0](https://github.com/dry-rb/dry-view/compare/v0.6.0...v0.7.0)
|
|
37
|
+
|
|
38
|
+
## 0.6.0 2019-01-30
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- [BREAKING] `Dry::View#call` now returns a `Dry::View::Rendered` instance, which carries both the rendered output (accessible via `#to_s` or `#to_str`) as well as all of the view's locals, wrapped in their view parts (accessible via `#locals` or individually via `#[]`) (timriley in [#72][pr72])
|
|
44
|
+
- [BREAKING] Added `Dry::View::PartBuilder` (renamed from `Dry::View::Decorator`), which resolves part classes from a namespace configured via View's `part_namespace` setting. A custom part builder can be specified via a View's `part_builder` setting. (timriley in [#80][pr80])
|
|
45
|
+
- [BREAKING] Context classes can now declare decorated attributes just like part classes, via `.decorate` class-level API. Context classes are now required to inherit from `Dry::View::Context`. `Dry::View::Context` provides a `#with` method for creating copies of itself while preserving the rendering details needed for decorated attributes to work (timriley in [#89][pr89] and [#91][pr91])
|
|
46
|
+
- Customizable _scope_ objects, which work like view parts, but instead of encapsulating a single value, they encapsulate a whole template or partial and all of its locals. Scopes can be created via `#scope` method in templates, parts, as well as scope classes themselves. Scope classes are resolved via a View's `scope_builder` setting, which defaults to an instance of `Dry::View::ScopeBuilder`.
|
|
47
|
+
- Added `inflector` setting to View, which is used by the part and scope builders to resolve classes for a given part or scope name. Defaults to `Dry::Inflector.new` (timriley in [#80][pr80] and [#90][pr90])
|
|
48
|
+
- Exposures can be sent to the layout template when defined with `layout: true` option (GustavoCaso in [#87][pr87])
|
|
49
|
+
- Exposures can be left undecorated by a part when defined with `decorate: false` option (timriley in [#88][pr88])
|
|
50
|
+
- Part classes have access to the current template format via a private `#_format` method (timriley in [#118][pr118])
|
|
51
|
+
- Added "Tilt adapter" layer, to ensure a rendering engine compatible with dry-view's features is being used. Added adapters for "haml" and "erb" templates to ensure that "hamlit-block" and "erbse" are required and used as engines (unlike their more common counterparts, both of these engines support the implicit block capturing that is a central part of dry-view rendering behaviour) (timriley in [#106][pr106])
|
|
52
|
+
- Added `renderer_engine_mapping` setting to View, which allows an explicit engine class to be provided for the rendering of a given type of template (e.g. `config.renderer_engine_mapping = {erb: Tilt::ErubiTemplate}`) (timriley in [#106][pr106])
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- Preserve renderer options when chdir-ing (timriley in [889ac7b](https://github.com/dry-rb/dry-view/commit/889ac7b))
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- [BREAKING] `Dry::View::Controller` renamed to `Dry::View` (timriley in [#115][pr115])
|
|
61
|
+
- [BREAKING] `Dry::View` `context` setting renamed to `default_context` (GustavoCaso in [#86][pr86])
|
|
62
|
+
- Exposure values are wrapped in their view parts before being made available as exposure dependencies (timriley in [#80][pr80])
|
|
63
|
+
- Exposures can access current context object through `context:` block or method parameter (timriley in [#119][pr119])
|
|
64
|
+
- Improved performance due to caching various lookups (timriley and GustavoCaso in [#97][pr97])
|
|
65
|
+
- `Part#inspect` output simplified to include only name and value (timriley in [#98][pr98])
|
|
66
|
+
- Attribute decoration in `Part` now achieved via a prepended module, which means it is possible to decorate an attribute provided by an instance method directly on the part class, which wasn't possible with the previous `method_missing`-based approach (timriley in [#110][pr110])
|
|
67
|
+
- `Part` classes can be initialized with missing `name:` and `rendering:` values, which can be useful for unit testing Part methods that don't use any rendering facilities (timriley in [#116][pr116])
|
|
68
|
+
|
|
69
|
+
[Compare v0.5.4...v0.6.0](https://github.com/dry-rb/dry-view/compare/v0.5.4...v0.6.0)
|
|
70
|
+
|
|
71
|
+
## 0.5.4 2019-01-06
|
|
72
|
+
|
|
73
|
+
This version was yanked due to the release accidentally containing a batch of breaking changes from master.
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- Preserve renderer options when chdir-ing (timriley in [889ac7b](https://github.com/dry-rb/dry-view/commit/889ac7b))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
[Compare v0.5.3...v0.5.4](https://github.com/dry-rb/dry-view/compare/v0.5.3...v0.5.4)
|
|
81
|
+
|
|
82
|
+
## 0.5.3 2018-10-22
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
### Added
|
|
86
|
+
|
|
87
|
+
- `renderer_options` setting for configuring tilt-based renderer (liseki in [#62][pr62])
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
- Part objects wrap values more transparently, via added `#respond_to_missing?` (liseki in [#63][pr63])
|
|
92
|
+
|
|
93
|
+
[Compare v0.5.2...v0.5.3](https://github.com/dry-rb/dry-view/compare/v0.5.2...v0.5.3)
|
|
94
|
+
|
|
95
|
+
## 0.5.2 2018-06-13
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
100
|
+
- Only truthy view part attributes are decorated (timriley)
|
|
101
|
+
|
|
102
|
+
[Compare v0.5.1...v0.5.2](https://github.com/dry-rb/dry-view/compare/v0.5.1...v0.5.2)
|
|
103
|
+
|
|
104
|
+
## 0.5.1 2018-02-20
|
|
105
|
+
|
|
2
106
|
|
|
3
107
|
### Added
|
|
4
108
|
|
|
5
109
|
- Exposures are inherited from parent view controller classes (GustavoCaso)
|
|
6
110
|
|
|
111
|
+
|
|
7
112
|
[Compare v0.5.0...v0.5.1](https://github.com/dry-rb/dry-view/compare/v0.5.0...v0.5.1)
|
|
8
113
|
|
|
9
|
-
|
|
114
|
+
## 0.5.0 2018-01-23
|
|
115
|
+
|
|
10
116
|
|
|
11
117
|
### Added
|
|
12
118
|
|
|
13
119
|
- Support for parts with decorated attributes (timriley + GustavoCaso)
|
|
14
120
|
- Ability to easily create another part instance via `Part#new` (GustavoCaso)
|
|
15
121
|
|
|
122
|
+
|
|
16
123
|
[Compare v0.4.0...v0.5.0](https://github.com/dry-rb/dry-view/compare/v0.4.0...v0.5.0)
|
|
17
124
|
|
|
18
|
-
|
|
125
|
+
## 0.4.0 2017-11-01
|
|
126
|
+
|
|
19
127
|
|
|
20
128
|
### Added
|
|
21
129
|
|
|
@@ -29,35 +137,52 @@
|
|
|
29
137
|
- Allow `Dry::View::Part` instances to be created without explicitly passing a `renderer`. This is helpful for unit testing view parts that don't need to render anything (dNitza)
|
|
30
138
|
- Partials can be nested within additional sub-directories by rendering them their relative path as their name, e.g. `render(:"foo/bar")` will look for a `foo/_bar.html.slim` template within the normal template lookup paths (timriley)
|
|
31
139
|
|
|
32
|
-
|
|
140
|
+
[Compare v0.3.0...v0.4.0](https://github.com/dry-rb/dry-view/compare/v0.3.0...v0.4.0)
|
|
141
|
+
|
|
142
|
+
## 0.3.0 2017-05-14
|
|
33
143
|
|
|
34
144
|
This release reintroduces view parts in a more helpful form. You can provide your own custom view part classes to encapsulate your view logic, as well as a decorator for custom, shared behavior arouund view part wrapping.
|
|
35
145
|
|
|
146
|
+
### Added
|
|
147
|
+
|
|
148
|
+
- Wrap all values passed to the template in `Dry::View::Part` objects
|
|
149
|
+
- Added a `decorator` config to `Dry::View::Controller`, with a default `Dry::View::Decorator` that wraps the exposure values in `Dry::View::Part` objects (as above). Provide your own part classes by passing an `:as` option to your exposures, e.g. `expose :user, as: MyApp::UserPart`
|
|
150
|
+
|
|
36
151
|
### Changed
|
|
37
152
|
|
|
38
153
|
- [BREAKING] Partial rendering in templates requires an explicit `render` method call instead of method_missing behaviour usinig the partial's name (e.g. `<%= render :my_partial %>` instead of `<%= my_partial %>`)
|
|
39
154
|
|
|
40
|
-
|
|
155
|
+
[Compare v0.2.2...v0.3.0](https://github.com/dry-rb/dry-view/compare/v0.2.2...v0.3.0)
|
|
41
156
|
|
|
42
|
-
|
|
43
|
-
- Added a `decorator` config to `Dry::View::Controller`, with a default `Dry::View::Decorator` that wraps the exposure values in `Dry::View::Part` objects (as above). Provide your own part classes by passing an `:as` option to your exposures, e.g. `expose :user, as: MyApp::UserPart`
|
|
157
|
+
## 0.2.2 2017-01-31
|
|
44
158
|
|
|
45
|
-
# 0.2.2 / 2017-01-31
|
|
46
159
|
|
|
47
160
|
### Changed
|
|
48
161
|
|
|
49
162
|
- Make input passthrough exposures (when there is no block or matching instance metod) return nil instead of raise in the case of a missing input key (timriley)
|
|
50
163
|
|
|
51
|
-
|
|
164
|
+
[Compare v0.2.1...v0.2.2](https://github.com/dry-rb/dry-view/compare/v0.2.1...v0.2.2)
|
|
165
|
+
|
|
166
|
+
## 0.2.1 2017-01-30
|
|
167
|
+
|
|
52
168
|
|
|
53
169
|
### Fixed
|
|
54
170
|
|
|
55
171
|
- Exposure blocks now have access to the view controller instance when they're called (timriley)
|
|
56
172
|
|
|
57
|
-
|
|
173
|
+
|
|
174
|
+
[Compare v0.2.0...v0.2.1](https://github.com/dry-rb/dry-view/compare/v0.2.0...v0.2.1)
|
|
175
|
+
|
|
176
|
+
## 0.2.0 2017-01-30
|
|
58
177
|
|
|
59
178
|
This release is a major reorientation for dry-view, and it should allow for more natural, straightforward template authoring.
|
|
60
179
|
|
|
180
|
+
### Added
|
|
181
|
+
|
|
182
|
+
- Will render templates using any Tilt-supported engine, based on the template's final file extension (e.g. `hello.html.slim` will use Slim). For thread-safety, be sure to explicitly require any engine gems you intend to use. (timriley)
|
|
183
|
+
- `expose` (and `expose_private`) `Dry::View::Controller` class methods allow you to more easily declare and prepare the data for your template (timriley)
|
|
184
|
+
- Added `Dry::View::Scope`, which is the scope used for rendering templates. This includes the data from the exposures along with the context object (timriley)
|
|
185
|
+
|
|
61
186
|
### Changed
|
|
62
187
|
|
|
63
188
|
- [BREAKING] `Dry::View::Layout` renamed to `Dry::View::Controller`. The "view controller" name better represents this object's job: to (timriley)
|
|
@@ -69,13 +194,10 @@ This release is a major reorientation for dry-view, and it should allow for more
|
|
|
69
194
|
- [BREAKING] With view parts removed, partials can only be rendered by top-level method calls within templates (timriley)
|
|
70
195
|
- Ruby version 2.1.0 is now the earliest supported version (timriley)
|
|
71
196
|
|
|
72
|
-
|
|
197
|
+
[Compare v0.1.1...v0.2.0](https://github.com/dry-rb/dry-view/compare/v0.1.1...v0.2.0)
|
|
73
198
|
|
|
74
|
-
|
|
75
|
-
- `expose` (and `expose_private`) `Dry::View::Controller` class methods allow you to more easily declare and prepare the data for your template (timriley)
|
|
76
|
-
- Added `Dry::View::Scope`, which is the scope used for rendering templates. This includes the data from the exposures along with the context object (timriley)
|
|
199
|
+
## 0.1.1 2016-07-07
|
|
77
200
|
|
|
78
|
-
# 0.1.1 / 2016-07-07
|
|
79
201
|
|
|
80
202
|
### Changed
|
|
81
203
|
|
|
@@ -83,12 +205,15 @@ This release is a major reorientation for dry-view, and it should allow for more
|
|
|
83
205
|
- Render template content first, before passing that content to the layout. This makes "content_for"-style behaviours possible, where the template stores some data that the layout can then use later (timriley)
|
|
84
206
|
- Configure default template encoding to be UTF-8, fixing some issues with template rendering on deployed sites (gotar)
|
|
85
207
|
|
|
86
|
-
|
|
208
|
+
[Compare v0.1.0...v0.1.1](https://github.com/dry-rb/dry-view/compare/v0.1.0...v0.1.1)
|
|
209
|
+
|
|
210
|
+
## 0.1.0 2016-03-28
|
|
211
|
+
|
|
87
212
|
|
|
88
213
|
### Added
|
|
89
214
|
|
|
90
|
-
– `Dry::View::Layout` supports multiple view template formats. Configure format/engine pairs (e.g. `{html: :slim, text: :erb}`) on the `formats` setting. The first format becomes the default. Request specific formats when calling the view, e.g. `my_view.call(format: :text)`.
|
|
215
|
+
- – `Dry::View::Layout` supports multiple view template formats. Configure format/engine pairs (e.g. `{html: :slim, text: :erb}`) on the `formats` setting. The first format becomes the default. Request specific formats when calling the view, e.g. `my_view.call(format: :text)`.
|
|
91
216
|
|
|
92
217
|
### Changed
|
|
93
218
|
|
|
94
|
-
– Extracted from rodakase and renamed to dry-view. `Rodakase::View` is now `Dry::View`.
|
|
219
|
+
- – Extracted from rodakase and renamed to dry-view. `Rodakase::View` is now `Dry::View`.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-2021 dry-rb team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
[gitter]: https://gitter.im/dry-rb/chat
|
|
2
1
|
[gem]: https://rubygems.org/gems/dry-view
|
|
3
|
-
[
|
|
4
|
-
[
|
|
2
|
+
[actions]: https://github.com/dry-rb/dry-view/actions
|
|
3
|
+
[codacy]: https://www.codacy.com/gh/dry-rb/dry-view
|
|
4
|
+
[chat]: https://dry-rb.zulipchat.com
|
|
5
|
+
[inchpages]: http://inch-ci.org/github/dry-rb/dry-view
|
|
5
6
|
|
|
6
|
-
# dry-view [][chat]
|
|
7
8
|
|
|
8
|
-
[][gem]
|
|
10
|
+
[][actions]
|
|
11
|
+
[][codacy]
|
|
12
|
+
[][codacy]
|
|
13
|
+
[][inchpages]
|
|
13
14
|
|
|
15
|
+
## Links
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
transformations, accepting user input and returning your rendered view.
|
|
17
|
+
* [User documentation](http://dry-rb.org/gems/dry-view)
|
|
18
|
+
* [API documentation](http://rubydoc.info/gems/dry-view)
|
|
18
19
|
|
|
19
|
-
##
|
|
20
|
+
## Supported Ruby versions
|
|
21
|
+
|
|
22
|
+
This library officially supports the following Ruby versions:
|
|
23
|
+
|
|
24
|
+
* MRI >= `2.5`
|
|
25
|
+
* jruby >= `9.2`
|
|
26
|
+
|
|
27
|
+
## License
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
See `LICENSE` file.
|
data/dry-view.gemspec
CHANGED
|
@@ -1,32 +1,40 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# this file is managed by dry-rb/devtools project
|
|
3
|
+
|
|
4
|
+
lib = File.expand_path('lib', __dir__)
|
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
6
|
require 'dry/view/version'
|
|
5
7
|
|
|
6
8
|
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name =
|
|
8
|
-
spec.
|
|
9
|
-
spec.
|
|
10
|
-
spec.
|
|
11
|
-
spec.
|
|
9
|
+
spec.name = 'dry-view'
|
|
10
|
+
spec.authors = ["Tim Riley", "Piotr Solnica"]
|
|
11
|
+
spec.email = ["tim@icelab.com.au", "piotr.solnica@gmail.com"]
|
|
12
|
+
spec.license = 'MIT'
|
|
13
|
+
spec.version = Dry::View::VERSION.dup
|
|
14
|
+
|
|
15
|
+
spec.summary = "A complete, standalone view rendering system that gives you everything you need to write well-factored view code"
|
|
12
16
|
spec.description = spec.summary
|
|
13
|
-
spec.homepage =
|
|
14
|
-
spec.
|
|
17
|
+
spec.homepage = 'https://dry-rb.org/gems/dry-view'
|
|
18
|
+
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-view.gemspec", "lib/**/*"]
|
|
19
|
+
spec.bindir = 'bin'
|
|
20
|
+
spec.executables = []
|
|
21
|
+
spec.require_paths = ['lib']
|
|
15
22
|
|
|
16
|
-
spec.
|
|
17
|
-
spec.
|
|
18
|
-
spec.
|
|
19
|
-
spec.
|
|
20
|
-
spec.require_paths = ["lib"]
|
|
23
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
24
|
+
spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-view/blob/master/CHANGELOG.md'
|
|
25
|
+
spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-view'
|
|
26
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-view/issues'
|
|
21
27
|
|
|
22
|
-
spec.required_ruby_version =
|
|
28
|
+
spec.required_ruby_version = ">= 2.5.0"
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
spec.add_runtime_dependency "
|
|
30
|
+
# to update dependencies edit project.yml
|
|
31
|
+
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
|
26
32
|
spec.add_runtime_dependency "dry-configurable", "~> 0.1"
|
|
27
|
-
spec.add_runtime_dependency "dry-
|
|
33
|
+
spec.add_runtime_dependency "dry-core", "~> 0.5", ">= 0.5"
|
|
34
|
+
spec.add_runtime_dependency "dry-inflector", "~> 0.1"
|
|
35
|
+
spec.add_runtime_dependency "tilt", "~> 2.0", ">= 2.0.6"
|
|
28
36
|
|
|
29
|
-
spec.add_development_dependency "bundler"
|
|
30
|
-
spec.add_development_dependency "rake"
|
|
31
|
-
spec.add_development_dependency "rspec"
|
|
37
|
+
spec.add_development_dependency "bundler"
|
|
38
|
+
spec.add_development_dependency "rake"
|
|
39
|
+
spec.add_development_dependency "rspec"
|
|
32
40
|
end
|
data/lib/dry-view.rb
CHANGED
data/lib/dry/view.rb
CHANGED
|
@@ -1,2 +1,514 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/configurable"
|
|
4
|
+
require "dry/core/cache"
|
|
5
|
+
require "dry/core/equalizer"
|
|
6
|
+
require "dry/inflector"
|
|
7
|
+
|
|
8
|
+
require_relative "view/context"
|
|
9
|
+
require_relative "view/exposures"
|
|
10
|
+
require_relative "view/errors"
|
|
11
|
+
require_relative "view/part_builder"
|
|
12
|
+
require_relative "view/path"
|
|
13
|
+
require_relative "view/render_environment"
|
|
14
|
+
require_relative "view/rendered"
|
|
15
|
+
require_relative "view/renderer"
|
|
16
|
+
require_relative "view/scope_builder"
|
|
17
|
+
|
|
18
|
+
# A collection of next-generation Ruby libraries, helping you to write clear,
|
|
19
|
+
# flexible, and more maintainable Ruby code. Each dry-rb gem fulfils a common
|
|
20
|
+
# task, and together they make a powerful platform for any kind of Ruby
|
|
21
|
+
# application.
|
|
22
|
+
module Dry
|
|
23
|
+
# A standalone, template-based view rendering system that offers everything
|
|
24
|
+
# you need to write well-factored view code.
|
|
25
|
+
#
|
|
26
|
+
# This represents a single view, holding the configuration and exposures
|
|
27
|
+
# necessary for rendering its template.
|
|
28
|
+
#
|
|
29
|
+
# @abstract Subclass this and provide your own configuration and exposures to
|
|
30
|
+
# define your own view (along with a custom `#initialize` if you wish to
|
|
31
|
+
# inject dependencies into your subclass)
|
|
32
|
+
#
|
|
33
|
+
# @see https://dry-rb.org/gems/dry-view/
|
|
34
|
+
#
|
|
35
|
+
# @api public
|
|
36
|
+
class View
|
|
37
|
+
# @api private
|
|
38
|
+
DEFAULT_RENDERER_OPTIONS = {default_encoding: "utf-8"}.freeze
|
|
39
|
+
|
|
40
|
+
include Dry::Equalizer(:config, :exposures)
|
|
41
|
+
|
|
42
|
+
extend Dry::Core::Cache
|
|
43
|
+
|
|
44
|
+
extend Dry::Configurable
|
|
45
|
+
|
|
46
|
+
# @!group Configuration
|
|
47
|
+
|
|
48
|
+
# @overload config.paths=(paths)
|
|
49
|
+
# Set an array of directories that will be searched for all templates
|
|
50
|
+
# (templates, partials, and layouts).
|
|
51
|
+
#
|
|
52
|
+
# These will be converted into Path objects and used for template lookup
|
|
53
|
+
# when rendering.
|
|
54
|
+
#
|
|
55
|
+
# This is a **required setting**.
|
|
56
|
+
#
|
|
57
|
+
# @param paths [String, Path, Array<String, Path>] the paths
|
|
58
|
+
#
|
|
59
|
+
# @api public
|
|
60
|
+
# @!scope class
|
|
61
|
+
setting :paths do |paths|
|
|
62
|
+
Array(paths).map { |path| Path[path] }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @overload config.template=(name)
|
|
66
|
+
# Set the name of the template for rendering this view. Template name
|
|
67
|
+
# should be relative to the configured `paths`.
|
|
68
|
+
#
|
|
69
|
+
# This is a **required setting**.
|
|
70
|
+
#
|
|
71
|
+
# @param name [String] template name
|
|
72
|
+
# @api public
|
|
73
|
+
# @!scope class
|
|
74
|
+
setting :template
|
|
75
|
+
|
|
76
|
+
# @overload config.layout=(name)
|
|
77
|
+
# Set the name of the layout to render templates within. Layouts will be
|
|
78
|
+
# looked up within the configured `layouts_dir`, within the configured
|
|
79
|
+
# `paths`.
|
|
80
|
+
#
|
|
81
|
+
# A false or nil value will use no layout. Defaults to `nil`.
|
|
82
|
+
#
|
|
83
|
+
# @param name [String, FalseClass, nil] layout name, or false to indicate no layout
|
|
84
|
+
# @api public
|
|
85
|
+
# @!scope class
|
|
86
|
+
setting :layout, false
|
|
87
|
+
|
|
88
|
+
# @overload config.layouts_dir=(dir)
|
|
89
|
+
# Set the name of the directory (within the configured `paths`) holding
|
|
90
|
+
# the layouts. Defaults to `"layouts"`
|
|
91
|
+
#
|
|
92
|
+
# @param dir [String] directory name
|
|
93
|
+
# @api public
|
|
94
|
+
# @!scope class
|
|
95
|
+
setting :layouts_dir, "layouts"
|
|
96
|
+
|
|
97
|
+
# @overload config.scope=(scope_class)
|
|
98
|
+
# Set the scope class to use when rendering the view's template.
|
|
99
|
+
#
|
|
100
|
+
# Configuring a custom scope class allows you to provide extra behaviour
|
|
101
|
+
# (alongside exposures) to the template.
|
|
102
|
+
#
|
|
103
|
+
# @see https://dry-rb.org/gems/dry-view/scopes/
|
|
104
|
+
#
|
|
105
|
+
# @param scope_class [Class] scope class (inheriting from `Dry::View::Scope`)
|
|
106
|
+
# @api public
|
|
107
|
+
# @!scope class
|
|
108
|
+
setting :scope
|
|
109
|
+
|
|
110
|
+
# @overload config.default_context=(context)
|
|
111
|
+
# Set the default context object to use when rendering. This will be used
|
|
112
|
+
# unless another context object is applied at render-time to `View#call`
|
|
113
|
+
#
|
|
114
|
+
# Defaults to a frozen instance of `Dry::View::Context`.
|
|
115
|
+
#
|
|
116
|
+
# @see View#call
|
|
117
|
+
#
|
|
118
|
+
# @param context [Dry::View::Context] context object
|
|
119
|
+
# @api public
|
|
120
|
+
# @!scope class
|
|
121
|
+
setting :default_context, Context.new.freeze
|
|
122
|
+
|
|
123
|
+
# @overload config.default_format=(format)
|
|
124
|
+
# Set the default format to use when rendering.
|
|
125
|
+
#
|
|
126
|
+
# Defaults to `:html`.
|
|
127
|
+
#
|
|
128
|
+
# @param format [Symbol]
|
|
129
|
+
# @api public
|
|
130
|
+
# @!scope class
|
|
131
|
+
setting :default_format, :html
|
|
132
|
+
|
|
133
|
+
# @overload config.scope_namespace=(namespace)
|
|
134
|
+
# Set a namespace that will be searched when building scope classes.
|
|
135
|
+
#
|
|
136
|
+
# @param namespace [Module, Class]
|
|
137
|
+
#
|
|
138
|
+
# @see Scope
|
|
139
|
+
#
|
|
140
|
+
# @api public
|
|
141
|
+
# @!scope class
|
|
142
|
+
setting :part_namespace
|
|
143
|
+
|
|
144
|
+
# @overload config.part_builder=(part_builder)
|
|
145
|
+
# Set a custom part builder class
|
|
146
|
+
#
|
|
147
|
+
# @see https://dry-rb.org/gems/dry-view/parts/
|
|
148
|
+
#
|
|
149
|
+
# @param part_builder [Class]
|
|
150
|
+
# @api public
|
|
151
|
+
# @!scope class
|
|
152
|
+
setting :part_builder, PartBuilder
|
|
153
|
+
|
|
154
|
+
# @overload config.scope_namespace=(namespace)
|
|
155
|
+
# Set a namespace that will be searched when building scope classes.
|
|
156
|
+
#
|
|
157
|
+
# @param namespace [Module, Class]
|
|
158
|
+
#
|
|
159
|
+
# @see Scope
|
|
160
|
+
#
|
|
161
|
+
# @api public
|
|
162
|
+
# @!scope class
|
|
163
|
+
setting :scope_namespace
|
|
164
|
+
|
|
165
|
+
# @overload config.scope_builder=(scope_builder)
|
|
166
|
+
# Set a custom scope builder class
|
|
167
|
+
#
|
|
168
|
+
# @see https://dry-rb.org/gems/dry-view/scopes/
|
|
169
|
+
#
|
|
170
|
+
# @param scope_builder [Class]
|
|
171
|
+
# @api public
|
|
172
|
+
# @!scope class
|
|
173
|
+
setting :scope_builder, ScopeBuilder
|
|
174
|
+
|
|
175
|
+
# @overload config.inflector=(inflector)
|
|
176
|
+
# Set an inflector to provide to the part_builder and scope_builder.
|
|
177
|
+
#
|
|
178
|
+
# Defaults to `Dry::Inflector.new`.
|
|
179
|
+
#
|
|
180
|
+
# @param inflector
|
|
181
|
+
# @api public
|
|
182
|
+
# @!scope class
|
|
183
|
+
setting :inflector, Dry::Inflector.new
|
|
184
|
+
|
|
185
|
+
# @overload config.renderer_options=(options)
|
|
186
|
+
# A hash of options to pass to the template engine. Template engines are
|
|
187
|
+
# provided by Tilt; see Tilt's documentation for what options your
|
|
188
|
+
# template engine may support.
|
|
189
|
+
#
|
|
190
|
+
# Defaults to `{default_encoding: "utf-8"}`. Any options passed will be
|
|
191
|
+
# merged onto the defaults.
|
|
192
|
+
#
|
|
193
|
+
# @see https://github.com/rtomayko/tilt
|
|
194
|
+
#
|
|
195
|
+
# @param options [Hash] renderer options
|
|
196
|
+
# @api public
|
|
197
|
+
# @!scope class
|
|
198
|
+
setting :renderer_options, DEFAULT_RENDERER_OPTIONS do |options|
|
|
199
|
+
DEFAULT_RENDERER_OPTIONS.merge(options.to_h).freeze
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# @overload config.renderer_engine_mapping=(mapping)
|
|
203
|
+
# A hash specifying the (Tilt-compatible) template engine class to use
|
|
204
|
+
# for a given format. Template engine detection is automatic based on
|
|
205
|
+
# format; use this setting only if you want to force a non-preferred
|
|
206
|
+
# engine.
|
|
207
|
+
#
|
|
208
|
+
# @example
|
|
209
|
+
# config.renderer_engine_mapping = {erb: Tilt::ErubiTemplate}
|
|
210
|
+
#
|
|
211
|
+
# @see https://github.com/rtomayko/tilt
|
|
212
|
+
#
|
|
213
|
+
# @param mapping [Hash<Symbol, Class>] engine mapping
|
|
214
|
+
# @api public
|
|
215
|
+
# @!scope class
|
|
216
|
+
setting :renderer_engine_mapping
|
|
217
|
+
|
|
218
|
+
# @!endgroup
|
|
219
|
+
|
|
220
|
+
# @api private
|
|
221
|
+
def self.inherited(klass)
|
|
222
|
+
super
|
|
223
|
+
exposures.each do |name, exposure|
|
|
224
|
+
klass.exposures.import(name, exposure)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# @!group Exposures
|
|
229
|
+
|
|
230
|
+
# @!macro [new] exposure_options
|
|
231
|
+
# @param options [Hash] the exposure's options
|
|
232
|
+
# @option options [Boolean] :layout expose this value to the layout (defaults to false)
|
|
233
|
+
# @option options [Boolean] :decorate decorate this value in a matching Part (defaults to
|
|
234
|
+
# true)
|
|
235
|
+
# @option options [Symbol, Class] :as an alternative name or class to use when finding a
|
|
236
|
+
# matching Part
|
|
237
|
+
|
|
238
|
+
# @overload expose(name, **options, &block)
|
|
239
|
+
# Define a value to be passed to the template. The return value of the
|
|
240
|
+
# block will be decorated by a matching Part and passed to the template.
|
|
241
|
+
#
|
|
242
|
+
# The block will be evaluated with the view instance as its `self`. The
|
|
243
|
+
# block's parameters will determine what it is given:
|
|
244
|
+
#
|
|
245
|
+
# - To receive other exposure values, provide positional parameters
|
|
246
|
+
# matching the exposure names. These exposures will already by decorated
|
|
247
|
+
# by their Parts.
|
|
248
|
+
# - To receive the view's input arguments (whatever is passed to
|
|
249
|
+
# `View#call`), provide matching keyword parameters. You can provide
|
|
250
|
+
# default values for these parameters to make the corresponding input
|
|
251
|
+
# keys optional
|
|
252
|
+
# - To receive the Context object, provide a `context:` keyword parameter
|
|
253
|
+
# - To receive the view's input arguments in their entirety, provide a
|
|
254
|
+
# keywords splat parameter (i.e. `**input`)
|
|
255
|
+
#
|
|
256
|
+
# @example Accessing input arguments
|
|
257
|
+
# expose :article do |slug:|
|
|
258
|
+
# article_repo.find_by_slug(slug)
|
|
259
|
+
# end
|
|
260
|
+
#
|
|
261
|
+
# @example Accessing other exposures
|
|
262
|
+
# expose :articles do
|
|
263
|
+
# article_repo.listing
|
|
264
|
+
# end
|
|
265
|
+
#
|
|
266
|
+
# expose :featured_articles do |articles|
|
|
267
|
+
# articles.select(&:featured?)
|
|
268
|
+
# end
|
|
269
|
+
#
|
|
270
|
+
# @param name [Symbol] name for the exposure
|
|
271
|
+
# @macro exposure_options
|
|
272
|
+
#
|
|
273
|
+
# @overload expose(name, **options)
|
|
274
|
+
# Define a value to be passed to the template, provided by an instance
|
|
275
|
+
# method matching the name. The method's return value will be decorated by
|
|
276
|
+
# a matching Part and passed to the template.
|
|
277
|
+
#
|
|
278
|
+
# The method's parameters will determine what it is given:
|
|
279
|
+
#
|
|
280
|
+
# - To receive other exposure values, provide positional parameters
|
|
281
|
+
# matching the exposure names. These exposures will already by decorated
|
|
282
|
+
# by their Parts.
|
|
283
|
+
# - To receive the view's input arguments (whatever is passed to
|
|
284
|
+
# `View#call`), provide matching keyword parameters. You can provide
|
|
285
|
+
# default values for these parameters to make the corresponding input
|
|
286
|
+
# keys optional
|
|
287
|
+
# - To receive the Context object, provide a `context:` keyword parameter
|
|
288
|
+
# - To receive the view's input arguments in their entirey, provide a
|
|
289
|
+
# keywords splat parameter (i.e. `**input`)
|
|
290
|
+
#
|
|
291
|
+
# @example Accessing input arguments
|
|
292
|
+
# expose :article
|
|
293
|
+
#
|
|
294
|
+
# def article(slug:)
|
|
295
|
+
# article_repo.find_by_slug(slug)
|
|
296
|
+
# end
|
|
297
|
+
#
|
|
298
|
+
# @example Accessing other exposures
|
|
299
|
+
# expose :articles
|
|
300
|
+
# expose :featured_articles
|
|
301
|
+
#
|
|
302
|
+
# def articles
|
|
303
|
+
# article_repo.listing
|
|
304
|
+
# end
|
|
305
|
+
#
|
|
306
|
+
# def featured_articles
|
|
307
|
+
# articles.select(&:featured?)
|
|
308
|
+
# end
|
|
309
|
+
#
|
|
310
|
+
# @param name [Symbol] name for the exposure
|
|
311
|
+
# @macro exposure_options
|
|
312
|
+
#
|
|
313
|
+
# @overload expose(name, **options)
|
|
314
|
+
# Define a single value to pass through from the input data (when there is
|
|
315
|
+
# no instance method matching the `name`). This value will be decorated by
|
|
316
|
+
# a matching Part and passed to the template.
|
|
317
|
+
#
|
|
318
|
+
# @param name [Symbol] name for the exposure
|
|
319
|
+
# @macro exposure_options
|
|
320
|
+
# @option options [Boolean] :default a default value to provide if there is no matching
|
|
321
|
+
# input data
|
|
322
|
+
#
|
|
323
|
+
# @overload expose(*names, **options)
|
|
324
|
+
# Define multiple values to pass through from the input data (when there
|
|
325
|
+
# is no instance methods matching their names). These values will be
|
|
326
|
+
# decorated by matching Parts and passed through to the template.
|
|
327
|
+
#
|
|
328
|
+
# The provided options will be applied to all the exposures.
|
|
329
|
+
#
|
|
330
|
+
# @param names [Symbol] names for the exposures
|
|
331
|
+
# @macro exposure_options
|
|
332
|
+
# @option options [Boolean] :default a default value to provide if there is no matching
|
|
333
|
+
# input data
|
|
334
|
+
#
|
|
335
|
+
# @see https://dry-rb.org/gems/dry-view/exposures/
|
|
336
|
+
#
|
|
337
|
+
# @api public
|
|
338
|
+
def self.expose(*names, **options, &block)
|
|
339
|
+
if names.length == 1
|
|
340
|
+
exposures.add(names.first, block, **options)
|
|
341
|
+
else
|
|
342
|
+
names.each do |name|
|
|
343
|
+
exposures.add(name, **options)
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# @api public
|
|
349
|
+
def self.private_expose(*names, **options, &block)
|
|
350
|
+
expose(*names, **options, private: true, &block)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Returns the defined exposures. These are unbound, since bound exposures
|
|
354
|
+
# are only created when initializing a View instance.
|
|
355
|
+
#
|
|
356
|
+
# @return [Exposures]
|
|
357
|
+
# @api private
|
|
358
|
+
def self.exposures
|
|
359
|
+
@exposures ||= Exposures.new
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# @!endgroup
|
|
363
|
+
|
|
364
|
+
# @!group Render environment
|
|
365
|
+
|
|
366
|
+
# Returns a render environment for the view and the given options. This
|
|
367
|
+
# environment isn't chdir'ed into any particular directory.
|
|
368
|
+
#
|
|
369
|
+
# @param format [Symbol] template format to use (defaults to the `default_format` setting)
|
|
370
|
+
# @param context [Context] context object to use (defaults to the `default_context` setting)
|
|
371
|
+
#
|
|
372
|
+
# @see View.template_env render environment for the view's template
|
|
373
|
+
# @see View.layout_env render environment for the view's layout
|
|
374
|
+
#
|
|
375
|
+
# @return [RenderEnvironment]
|
|
376
|
+
# @api public
|
|
377
|
+
def self.render_env(format: config.default_format, context: config.default_context)
|
|
378
|
+
RenderEnvironment.prepare(renderer(format), config, context)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# @overload template_env(format: config.default_format, context: config.default_context)
|
|
382
|
+
# Returns a render environment for the view and the given options,
|
|
383
|
+
# chdir'ed into the view's template directory. This is the environment
|
|
384
|
+
# used when rendering the template, and is useful to to fetch
|
|
385
|
+
# independently when unit testing Parts and Scopes.
|
|
386
|
+
#
|
|
387
|
+
# @param format [Symbol] template format to use (defaults to the `default_format` setting)
|
|
388
|
+
# @param context [Context] context object to use (defaults to the `default_context` setting)
|
|
389
|
+
#
|
|
390
|
+
# @return [RenderEnvironment]
|
|
391
|
+
# @api public
|
|
392
|
+
def self.template_env(**args)
|
|
393
|
+
render_env(**args).chdir(config.template)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# @overload layout_env(format: config.default_format, context: config.default_context)
|
|
397
|
+
# Returns a render environment for the view and the given options,
|
|
398
|
+
# chdir'ed into the view's layout directory. This is the environment used
|
|
399
|
+
# when rendering the view's layout.
|
|
400
|
+
#
|
|
401
|
+
# @param format [Symbol] template format to use (defaults to the `default_format` setting)
|
|
402
|
+
# @param context [Context] context object to use (defaults to the `default_context` setting)
|
|
403
|
+
#
|
|
404
|
+
# @return [RenderEnvironment] @api public
|
|
405
|
+
def self.layout_env(**args)
|
|
406
|
+
render_env(**args).chdir(layout_path)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Returns renderer for the view and provided format
|
|
410
|
+
#
|
|
411
|
+
# @api private
|
|
412
|
+
def self.renderer(format)
|
|
413
|
+
fetch_or_store(:renderer, config, format) {
|
|
414
|
+
Renderer.new(
|
|
415
|
+
config.paths,
|
|
416
|
+
format: format,
|
|
417
|
+
engine_mapping: config.renderer_engine_mapping,
|
|
418
|
+
**config.renderer_options
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# @api private
|
|
424
|
+
def self.layout_path
|
|
425
|
+
File.join(config.layouts_dir, config.layout)
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# @!endgroup
|
|
429
|
+
|
|
430
|
+
# The view's bound exposures
|
|
431
|
+
#
|
|
432
|
+
# @return [Exposures]
|
|
433
|
+
# @api private
|
|
434
|
+
attr_reader :exposures
|
|
435
|
+
|
|
436
|
+
# Returns an instance of the view. This binds the defined exposures to the
|
|
437
|
+
# view instance.
|
|
438
|
+
#
|
|
439
|
+
# Subclasses can define their own `#initialize` to accept injected
|
|
440
|
+
# dependencies, but must call `super()` to ensure the standard view
|
|
441
|
+
# initialization can proceed.
|
|
442
|
+
#
|
|
443
|
+
# @api public
|
|
444
|
+
def initialize
|
|
445
|
+
@exposures = self.class.exposures.bind(self)
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# The view's configuration
|
|
449
|
+
#
|
|
450
|
+
# @api private
|
|
451
|
+
def config
|
|
452
|
+
self.class.config
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Render the view
|
|
456
|
+
#
|
|
457
|
+
# @param format [Symbol] template format to use
|
|
458
|
+
# @param context [Context] context object to use
|
|
459
|
+
# @param input input data for preparing exposure values
|
|
460
|
+
#
|
|
461
|
+
# @return [Rendered] rendered view object
|
|
462
|
+
# @api public
|
|
463
|
+
def call(format: config.default_format, context: config.default_context, **input)
|
|
464
|
+
ensure_config
|
|
465
|
+
|
|
466
|
+
env = self.class.render_env(format: format, context: context)
|
|
467
|
+
template_env = self.class.template_env(format: format, context: context)
|
|
468
|
+
|
|
469
|
+
locals = locals(template_env, input)
|
|
470
|
+
output = env.template(config.template, template_env.scope(config.scope, locals))
|
|
471
|
+
|
|
472
|
+
if layout?
|
|
473
|
+
layout_env = self.class.layout_env(format: format, context: context)
|
|
474
|
+
output = env.template(
|
|
475
|
+
self.class.layout_path,
|
|
476
|
+
layout_env.scope(config.scope, layout_locals(locals))
|
|
477
|
+
) { output }
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
Rendered.new(output: output, locals: locals)
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
private
|
|
484
|
+
|
|
485
|
+
# @api private
|
|
486
|
+
def ensure_config
|
|
487
|
+
raise UndefinedConfigError, :paths unless Array(config.paths).any?
|
|
488
|
+
raise UndefinedConfigError, :template unless config.template
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# @api private
|
|
492
|
+
def locals(render_env, input)
|
|
493
|
+
exposures.(context: render_env.context, **input) do |value, exposure|
|
|
494
|
+
if exposure.decorate? && value
|
|
495
|
+
render_env.part(exposure.name, value, **exposure.options)
|
|
496
|
+
else
|
|
497
|
+
value
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# @api private
|
|
503
|
+
def layout_locals(locals)
|
|
504
|
+
locals.each_with_object({}) do |(key, value), layout_locals|
|
|
505
|
+
layout_locals[key] = value if exposures[key].for_layout?
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
# @api private
|
|
510
|
+
def layout?
|
|
511
|
+
!!config.layout # rubocop:disable Style/DoubleNegation
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
end
|