pakyow-presenter 1.0.0.rc2 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pakyow/{presenter/actions → actions/presenter}/auto_render.rb +2 -2
  3. data/lib/pakyow/plugin/helpers/rendering.rb +15 -2
  4. data/lib/pakyow/presenter.rb +1 -1
  5. data/lib/pakyow/presenter/attributes.rb +8 -0
  6. data/lib/pakyow/presenter/attributes/attribute.rb +0 -1
  7. data/lib/pakyow/presenter/attributes/boolean.rb +0 -1
  8. data/lib/pakyow/presenter/behavior/error_rendering.rb +1 -0
  9. data/lib/pakyow/presenter/behavior/initializing.rb +1 -1
  10. data/lib/pakyow/presenter/behavior/modes.rb +1 -0
  11. data/lib/pakyow/presenter/binder.rb +2 -0
  12. data/lib/pakyow/presenter/binding_parts.rb +1 -0
  13. data/lib/pakyow/presenter/component.rb +1 -4
  14. data/lib/pakyow/presenter/composers/component.rb +1 -0
  15. data/lib/pakyow/presenter/composers/view.rb +1 -0
  16. data/lib/pakyow/presenter/errors.rb +2 -2
  17. data/lib/pakyow/presenter/framework.rb +22 -25
  18. data/lib/pakyow/presenter/presenter.rb +5 -0
  19. data/lib/pakyow/presenter/presenters/endpoint.rb +3 -3
  20. data/lib/pakyow/presenter/presenters/form.rb +5 -5
  21. data/lib/pakyow/presenter/processor.rb +42 -38
  22. data/lib/pakyow/presenter/renderer.rb +6 -1
  23. data/lib/pakyow/presenter/renderer/behavior/cleanup_prototype_nodes.rb +23 -0
  24. data/lib/pakyow/presenter/renderer/behavior/cleanup_unbound_bindings.rb +37 -0
  25. data/lib/pakyow/presenter/renderer/behavior/create_template_nodes.rb +29 -0
  26. data/lib/pakyow/presenter/renderer/behavior/insert_prototype_bar.rb +103 -0
  27. data/lib/pakyow/presenter/renderer/behavior/install_authenticity.rb +44 -0
  28. data/lib/pakyow/presenter/renderer/behavior/place_in_mode.rb +58 -0
  29. data/lib/pakyow/presenter/renderer/behavior/render_components.rb +281 -0
  30. data/lib/pakyow/presenter/renderer/behavior/set_page_title.rb +37 -0
  31. data/lib/pakyow/presenter/renderer/behavior/setup_endpoints.rb +64 -0
  32. data/lib/pakyow/presenter/renderer/behavior/setup_forms.rb +176 -0
  33. data/lib/pakyow/presenter/significant_nodes.rb +2 -2
  34. data/lib/pakyow/presenter/templates.rb +24 -15
  35. data/lib/pakyow/presenter/versioned_view.rb +1 -0
  36. data/lib/pakyow/presenter/view.rb +11 -9
  37. data/lib/pakyow/presenter/views/form.rb +39 -35
  38. data/lib/pakyow/presenter/views/layout.rb +20 -18
  39. data/lib/pakyow/presenter/views/page.rb +47 -45
  40. data/lib/pakyow/presenter/views/partial.rb +17 -15
  41. data/lib/string_doc.rb +3 -1
  42. data/lib/string_doc/attributes.rb +1 -0
  43. data/lib/string_doc/meta_attributes.rb +1 -0
  44. data/lib/string_doc/meta_node.rb +1 -0
  45. data/lib/string_doc/node.rb +1 -0
  46. metadata +19 -20
  47. data/lib/pakyow/presenter/presentable_error.rb +0 -19
  48. data/lib/pakyow/presenter/rendering/actions/cleanup_prototype_nodes.rb +0 -21
  49. data/lib/pakyow/presenter/rendering/actions/cleanup_unbound_bindings.rb +0 -35
  50. data/lib/pakyow/presenter/rendering/actions/create_template_nodes.rb +0 -27
  51. data/lib/pakyow/presenter/rendering/actions/insert_prototype_bar.rb +0 -101
  52. data/lib/pakyow/presenter/rendering/actions/install_authenticity.rb +0 -42
  53. data/lib/pakyow/presenter/rendering/actions/place_in_mode.rb +0 -56
  54. data/lib/pakyow/presenter/rendering/actions/render_components.rb +0 -279
  55. data/lib/pakyow/presenter/rendering/actions/set_page_title.rb +0 -35
  56. data/lib/pakyow/presenter/rendering/actions/setup_endpoints.rb +0 -62
  57. data/lib/pakyow/presenter/rendering/actions/setup_forms.rb +0 -174
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pakyow/error"
4
-
5
- module Pakyow
6
- class Error
7
- def [](key)
8
- if respond_to?(key)
9
- public_send(key)
10
- else
11
- nil
12
- end
13
- end
14
-
15
- def include?(key)
16
- respond_to?(key)
17
- end
18
- end
19
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pakyow/support/extension"
4
-
5
- module Pakyow
6
- module Presenter
7
- module Actions
8
- module CleanupPrototypeNodes
9
- extend Support::Extension
10
-
11
- apply_extension do
12
- build do |view|
13
- unless Pakyow.env?(:prototype)
14
- view.object.each_significant_node(:prototype, descend: true).map(&:itself).each(&:remove)
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pakyow/support/extension"
4
-
5
- module Pakyow
6
- module Presenter
7
- module Actions
8
- module CleanupUnboundBindings
9
- extend Support::Extension
10
-
11
- apply_extension do
12
- attach do |presenter|
13
- unless Pakyow.env?(:prototype)
14
- # Cleanup unbound bindings. We don't do this in prototype mode because it's important
15
- # for the prototype to be complete, showing everything to the designer.
16
- #
17
- presenter.render node: -> {
18
- object.find_significant_nodes(:binding, descend: true).map { |node|
19
- View.from_object(node)
20
- }
21
- }, priority: :low do
22
- # We check that the node is still labeled as a binding in case the node was replaced
23
- # during a previous transformation with a node that isn't a binding.
24
- #
25
- unless !view.object.labeled?(:binding) || view.object.labeled?(:bound) || view.object.labeled?(:failed) || view.object.label(:version) == :empty
26
- remove
27
- end
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pakyow/support/extension"
4
-
5
- module Pakyow
6
- module Presenter
7
- module Actions
8
- module CreateTemplateNodes
9
- extend Support::Extension
10
-
11
- apply_extension do
12
- build do |view|
13
- unless Pakyow.env?(:prototype)
14
- view.each_binding_scope(descend: true) do |node_with_binding|
15
- attributes = node_with_binding.attributes.attributes_hash.each_with_object({}) do |(attribute, value), acc|
16
- acc[attribute] = value if attribute.to_s.start_with?("data")
17
- end
18
-
19
- node_with_binding.after("<script type=\"text/template\"#{StringDoc::Attributes.new(attributes).to_s}>#{node_with_binding.render}</script>")
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,101 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pakyow/support/extension"
4
-
5
- module Pakyow
6
- module Presenter
7
- module Actions
8
- module InsertPrototypeBar
9
- extend Support::Extension
10
-
11
- apply_extension do
12
- attach do |presenter|
13
- if Pakyow.env?(:prototype)
14
- presenter.render node: -> {
15
- if body = object.find_first_significant_node(:body)
16
- View.from_object(body)
17
- end
18
- } do
19
- view.object.append_html <<~HTML
20
- <style>
21
- .pw-prototype {
22
- font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
23
- display:flex;
24
- align-items: center;
25
- position: fixed;
26
- z-index: 1000;
27
- right: 0;
28
- bottom: 0;
29
- background: #156eed;
30
- color: #fff;
31
- font-size: 11px;
32
- line-height: 11px;
33
- font-weight: 500;
34
- border-top-left-radius: 1px;
35
- padding-left: 5px;
36
- }
37
-
38
- .pw-prototype-tag {
39
- background: #ff8b6c;
40
- color: #fff;
41
- text-transform: uppercase;
42
- font-size: 10px;
43
- line-height: 12px;
44
- font-weight: 600;
45
- padding: 5px 5px 4px 5px;
46
- margin-left: 10px;
47
- }
48
- </style>
49
-
50
- <div class="pw-prototype">
51
- #{InsertPrototypeBar.ui_modes_html(view, __modes || [:default])}
52
-
53
- <div class="pw-prototype-tag">
54
- Prototype
55
- </div>
56
- </div>
57
- HTML
58
- end
59
- end
60
- end
61
-
62
- expose do |connection|
63
- if Pakyow.env?(:prototype)
64
- connection.set(:__modes, connection.params[:modes])
65
- end
66
- end
67
- end
68
-
69
- # @api private
70
- def self.ui_modes_html(view, current_modes)
71
- current_modes = current_modes.map(&:to_sym)
72
-
73
- modes = view.object.each_significant_node(:mode).map { |node|
74
- node.label(:mode)
75
- }
76
-
77
- modes.unshift(
78
- (view.info(:mode) || :default).to_sym
79
- ).uniq!
80
-
81
- options = modes.map { |each_mode|
82
- selected = if current_modes.include?(each_mode)
83
- " selected=\"selected\""
84
- else
85
- ""
86
- end
87
-
88
- nice_mode = Support.inflector.humanize(Support.inflector.underscore(each_mode))
89
- "<option value=\"#{each_mode}\"#{selected}>#{nice_mode}</option>"
90
- }.join
91
-
92
- <<~HTML
93
- UI Mode: <select onchange="document.location = window.location.pathname + '?modes[]=' + this.value " style="-webkit-appearance: none; -moz-appearance: none; -ms-appearance: none; -o-appearance: none; appearance: none; font-size: 11px; font-weight: 500; line-height: 20px; background: none; border: none; color: #fff; outline: none; margin: 0; margin-left: 5px;">
94
- #{options}
95
- </select>
96
- HTML
97
- end
98
- end
99
- end
100
- end
101
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pakyow/support/extension"
4
- require "pakyow/support/message_verifier"
5
- require "pakyow/support/safe_string"
6
-
7
- module Pakyow
8
- module Presenter
9
- module Actions
10
- module InstallAuthenticity
11
- extend Support::Extension
12
-
13
- apply_extension do
14
- build do |view, app:|
15
- if app.config.presenter.embed_authenticity_token && head = view.head
16
- head.append(Support::SafeStringHelpers.html_safe("<meta name=\"pw-authenticity-token\">"))
17
- head.append(Support::SafeStringHelpers.html_safe("<meta name=\"pw-authenticity-param\" content=\"#{app.config.security.csrf.param}\">"))
18
- end
19
- end
20
-
21
- attach do |presenter|
22
- presenter.render node: -> {
23
- node = object.each_significant_node(:meta).find { |meta_node|
24
- meta_node.attributes[:name] == "pw-authenticity-token"
25
- }
26
-
27
- unless node.nil?
28
- View.from_object(node)
29
- end
30
- } do
31
- attributes[:content] = @presentables[:__verifier].sign(Support::MessageVerifier.key)
32
- end
33
- end
34
-
35
- expose do |connection|
36
- connection.set(:__verifier, connection.verifier)
37
- end
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pakyow/support/extension"
4
-
5
- module Pakyow
6
- module Presenter
7
- module Actions
8
- module PlaceInMode
9
- extend Support::Extension
10
-
11
- apply_extension do
12
- build do |view, modes:|
13
- unless Pakyow.env?(:prototype)
14
- PlaceInMode.perform(view, modes)
15
- end
16
- end
17
-
18
- attach do |presenter|
19
- if Pakyow.env?(:prototype)
20
- presenter.render node: -> {
21
- object.find_significant_nodes(:mode, descend: true).map { |node|
22
- View.from_object(node)
23
- }
24
- } do
25
- PlaceInMode.perform(view, __modes)
26
- end
27
- end
28
- end
29
-
30
- expose do |connection|
31
- if Pakyow.env?(:prototype)
32
- connection.set(:__modes, connection.params[:modes] || [:default])
33
- end
34
- end
35
- end
36
-
37
- # @api private
38
- def self.perform(view, modes)
39
- if modes.length == 1 && modes.first.to_sym == :default
40
- modes = view.info(:modes) || modes
41
- end
42
-
43
- modes.map!(&:to_sym)
44
-
45
- if view.object.is_a?(StringDoc::Node) && view.object.significant?(:mode) && !modes.include?(view.object.label(:mode))
46
- view.remove
47
- else
48
- view.object.each_significant_node(:mode, descend: true).select { |node|
49
- !modes.include?(node.label(:mode))
50
- }.each(&:remove)
51
- end
52
- end
53
- end
54
- end
55
- end
56
- end
@@ -1,279 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pakyow/support/extension"
4
-
5
- require "pakyow/presenter/composers/component"
6
-
7
- module Pakyow
8
- module Presenter
9
- module Actions
10
- module RenderComponents
11
- extend Support::Extension
12
-
13
- apply_extension do
14
- build do |view, app:, composer:, modes:|
15
- unless Pakyow.env?(:prototype)
16
- initial_path = case composer
17
- when Composers::Component
18
- composer.component_path
19
- else
20
- []
21
- end
22
-
23
- component_view = case composer
24
- when Composers::Component
25
- composer.class.follow_path(composer.component_path, view)
26
- else
27
- view
28
- end
29
-
30
- RenderComponents.initialize_renderable_components(
31
- component_view, app: app, composer: composer, modes: modes, path: initial_path
32
- )
33
- end
34
- end
35
-
36
- expose do |connection|
37
- # Prevent state from leaking from the component to the rest of the app.
38
- #
39
- component_connection = connection.dup
40
-
41
- # Expose the component connection for performing from each component.
42
- #
43
- connection.set(:__component_connection, component_connection)
44
- end
45
- end
46
-
47
- # @api private
48
- def self.initialize_renderable_components(view, app:, composer:, modes:, path: [])
49
- view.components.each_with_index do |component_view, i|
50
- current_path = path.dup
51
- current_path << i
52
-
53
- # If view will be rendered from the app, look for the component on the app.
54
- #
55
- component_state = if app.is_a?(Plugin) && app.parent.view?(composer.view_path)
56
- app.parent.state(:component)
57
- else
58
- app.state(:component)
59
- end
60
-
61
- components = component_view.object.label(:components).each_with_object([]) { |component_label, arr|
62
- component_class = component_state.find { |component|
63
- component.__object_name.name == component_label[:name]
64
- }
65
-
66
- if component_class
67
- # Turn the component into a renderable component. Once an instance is attached on the
68
- # backend, the component will not be traversed by renders from its parent instead being
69
- # rendered by its own renderer instance.
70
- #
71
- # We don't want the same restriction for non-renderable components because a change to
72
- # the view should not affect how things work on the backend.
73
- #
74
- component_label[:renderable] = true
75
-
76
- arr << {
77
- class: component_class,
78
- config: component_label[:config]
79
- }
80
- end
81
- }
82
-
83
- if components.any?
84
- # Since one or more attached components is renderable, we no longer want to descend.
85
- #
86
- component_view.object.set_label(:descend, false)
87
-
88
- # Define the render function that calls the component and renders it at render time.
89
- #
90
- component_render = app.isolated(:Presenter).send(:render_proc, component_view) { |node, _context, string|
91
- presentable_component_connection = presentables[:__component_connection]
92
- component_connection = presentable_component_connection.dup
93
-
94
- components.each do |component|
95
- presentables.each do |key, value|
96
- if key.to_s.start_with?("__")
97
- component_connection.set(key, value)
98
- end
99
- end
100
-
101
- # If the component was defined in an app but being called inside a plugin, set the app to the app instead of the plugin.
102
- #
103
- if component_connection.app.is_a?(Plugin) && component[:class].ancestors.include?(component_connection.app.parent.isolated(:Component))
104
- component_connection = component_connection.class.from_connection(component_connection, :@app => component_connection.app.parent)
105
- end
106
-
107
- component_instance = component[:class].new(
108
- connection: component_connection,
109
- config: component[:config]
110
- )
111
-
112
- # Call the component.
113
- #
114
- component_instance.perform
115
- end
116
-
117
- # Build a compound component presenter.
118
- #
119
- component_presenter = if components.length > 1
120
- RenderComponents.find_compound_presenter(
121
- app, components.map { |c| c[:class] }
122
- )
123
- else
124
- components.first[:class].__presenter_class
125
- end
126
-
127
- # Setup the renderer for the component.
128
- #
129
- renderer = app.isolated(:Renderer).new(
130
- app: app,
131
- presentables: component_connection.values,
132
- presenter_class: component_presenter,
133
- composer: Composers::Component.new(
134
- composer.view_path, current_path, app: app, labels: node.labels
135
- ),
136
- modes: modes
137
- )
138
-
139
- # Render to the main buffer.
140
- #
141
- renderer.perform(string)
142
-
143
- # Return nil so nothing else gets written.
144
- #
145
- nil
146
- }
147
-
148
- # Attach the above render function to the render node.
149
- #
150
- component_view.object.transform do |node, context, string|
151
- component_render.call(node, context, string); nil
152
- end
153
- else
154
- initialize_renderable_components(
155
- component_view, app: app, composer: composer, modes: modes, path: current_path
156
- )
157
- end
158
- end
159
- end
160
-
161
- # @api private
162
- def self.find_renderable_components(view, components = [])
163
- view.components.each do |component_view|
164
- find_renderable_components(component_view, components)
165
-
166
- if component_view.object.label(:components).any? { |c| c[:renderable] }
167
- components << component_view
168
- end
169
- end
170
-
171
- components
172
- end
173
-
174
- # @api private
175
- def self.wrap_block(block, context_class)
176
- Proc.new do
177
- @app.presenter_for_context(
178
- context_class.__presenter_class, self
179
- ).instance_eval(&block)
180
- end
181
- end
182
-
183
- # @api private
184
- def self.find_compound_presenter(app, component_classes)
185
- compound_name = component_classes.map { |component_class|
186
- component_class.__object_name.name.to_s
187
- }.join("_")
188
-
189
- object_name = Support::ObjectName.namespace(
190
- app.class.__object_name.namespace.parts[0], :components, compound_name, :presenter
191
- )
192
-
193
- if const_defined?(object_name.constant)
194
- const_get(object_name.constant)
195
- else
196
- nil
197
- end
198
- end
199
-
200
- # @api private
201
- #
202
- def self.find_or_build_compound_presenter(app, component_classes)
203
- compound_name = component_classes.map { |component_class|
204
- component_class.__object_name.name.to_s
205
- }.join("_")
206
-
207
- object_name = Support::ObjectName.namespace(
208
- app.class.__object_name.namespace.parts[0], :components, compound_name, :presenter
209
- )
210
-
211
- if const_defined?(object_name.constant)
212
- const_get(object_name.constant)
213
- else
214
- component_presenter = Class.new(app.isolated(:Presenter))
215
- Support::ObjectMaker.define_const_for_object_with_name(component_presenter, object_name)
216
-
217
- component_classes.each do |component_class|
218
- # Copy unique attached renders.
219
- #
220
- component_class.__presenter_class.__attached_renders.each_with_index do |attached_render, i|
221
- component_presenter.__attached_renders.insert(i, {
222
- binding_path: attached_render[:binding_path],
223
- channel: attached_render[:channel],
224
- node: attached_render[:node],
225
- priority: attached_render[:priority],
226
- block: wrap_block(attached_render[:block], component_class),
227
- })
228
- end
229
-
230
- # Copy unique global options.
231
- #
232
- component_class.__presenter_class.__global_options.each do |form_binding, field_binding_values|
233
- field_binding_values.each do |field_binding, field_binding_value|
234
- component_presenter.options_for(
235
- form_binding,
236
- field_binding,
237
- field_binding_value[:options],
238
- &wrap_block(field_binding_value[:block], component_class)
239
- )
240
- end
241
- end
242
-
243
- # Copy unique presentation logic.
244
- #
245
- component_class.__presenter_class.__presentation_logic.each do |binding_name, logic_arr|
246
- unless component_presenter.__presentation_logic.include?(binding_name)
247
- component_presenter.__presentation_logic[binding_name] = []
248
- end
249
-
250
- logic_arr.each_with_index do |logic, i|
251
- component_presenter.__presentation_logic[binding_name].insert(i, {
252
- block: wrap_block(logic[:block], component_class),
253
- channel: logic[:channel]
254
- })
255
- end
256
- end
257
-
258
- # Copy unique versioning logic.
259
- #
260
- component_class.__presenter_class.__versioning_logic.each do |binding_name, logic_arr|
261
- unless component_presenter.__versioning_logic.include?(binding_name)
262
- component_presenter.__versioning_logic[binding_name] = []
263
- end
264
-
265
- logic_arr.each_with_index do |logic, i|
266
- component_presenter.__versioning_logic[binding_name].insert(i, {
267
- block: wrap_block(logic[:block], component_class)
268
- })
269
- end
270
- end
271
- end
272
-
273
- component_presenter
274
- end
275
- end
276
- end
277
- end
278
- end
279
- end