pakyow-presenter 0.11.3 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +5 -5
  2. data/{pakyow-presenter/CHANGELOG.md → CHANGELOG.md} +0 -5
  3. data/LICENSE +4 -0
  4. data/{pakyow-presenter/README.md → README.md} +1 -2
  5. data/lib/pakyow/generators/presenter/presenter_generator.rb +28 -0
  6. data/lib/pakyow/generators/presenter/templates/presenter.rb.tt +2 -0
  7. data/lib/pakyow/plugin/helpers/rendering.rb +19 -0
  8. data/lib/pakyow/presenter/actions/auto_render.rb +15 -0
  9. data/lib/pakyow/presenter/attributes/attribute.rb +22 -0
  10. data/lib/pakyow/presenter/attributes/boolean.rb +24 -0
  11. data/lib/pakyow/presenter/attributes/hash.rb +89 -0
  12. data/lib/pakyow/presenter/attributes/set.rb +61 -0
  13. data/lib/pakyow/presenter/attributes/string.rb +38 -0
  14. data/lib/pakyow/presenter/attributes.rb +126 -0
  15. data/lib/pakyow/presenter/behavior/config.rb +29 -0
  16. data/lib/pakyow/presenter/behavior/error_rendering.rb +124 -0
  17. data/lib/pakyow/presenter/behavior/exposures.rb +29 -0
  18. data/lib/pakyow/presenter/behavior/implicit_rendering.rb +21 -0
  19. data/lib/pakyow/presenter/behavior/initializing.rb +37 -0
  20. data/lib/pakyow/presenter/behavior/modes.rb +117 -0
  21. data/lib/pakyow/presenter/behavior/versions.rb +71 -0
  22. data/lib/pakyow/presenter/behavior/watching.rb +21 -0
  23. data/lib/pakyow/presenter/binder.rb +115 -0
  24. data/lib/pakyow/presenter/binding_parts.rb +64 -0
  25. data/lib/pakyow/presenter/component.rb +74 -0
  26. data/lib/pakyow/presenter/composers/component.rb +78 -0
  27. data/lib/pakyow/presenter/composers/view.rb +73 -0
  28. data/lib/pakyow/presenter/errors.rb +63 -0
  29. data/lib/pakyow/presenter/framework.rb +203 -0
  30. data/lib/pakyow/presenter/front_matter_parser.rb +42 -0
  31. data/lib/pakyow/presenter/helpers/rendering.rb +18 -0
  32. data/lib/pakyow/presenter/presentable.rb +28 -0
  33. data/lib/pakyow/presenter/presentable_error.rb +19 -0
  34. data/lib/pakyow/presenter/presenter.rb +766 -0
  35. data/lib/pakyow/presenter/presenters/endpoint.rb +158 -0
  36. data/lib/pakyow/presenter/presenters/form.rb +522 -0
  37. data/lib/pakyow/presenter/processor.rb +61 -0
  38. data/lib/pakyow/presenter/renderable.rb +28 -0
  39. data/lib/pakyow/presenter/renderer.rb +269 -0
  40. data/lib/pakyow/presenter/rendering/actions/cleanup_prototype_nodes.rb +21 -0
  41. data/lib/pakyow/presenter/rendering/actions/cleanup_unbound_bindings.rb +35 -0
  42. data/lib/pakyow/presenter/rendering/actions/create_template_nodes.rb +27 -0
  43. data/lib/pakyow/presenter/rendering/actions/insert_prototype_bar.rb +101 -0
  44. data/lib/pakyow/presenter/rendering/actions/install_authenticity.rb +42 -0
  45. data/lib/pakyow/presenter/rendering/actions/place_in_mode.rb +56 -0
  46. data/lib/pakyow/presenter/rendering/actions/render_components.rb +279 -0
  47. data/lib/pakyow/presenter/rendering/actions/set_page_title.rb +35 -0
  48. data/lib/pakyow/presenter/rendering/actions/setup_endpoints.rb +62 -0
  49. data/lib/pakyow/presenter/rendering/actions/setup_forms.rb +174 -0
  50. data/lib/pakyow/presenter/significant_nodes.rb +309 -0
  51. data/lib/pakyow/presenter/templates.rb +229 -0
  52. data/lib/pakyow/presenter/versioned_view.rb +209 -0
  53. data/lib/pakyow/presenter/view.rb +586 -0
  54. data/lib/pakyow/presenter/views/form.rb +58 -0
  55. data/lib/pakyow/presenter/views/layout.rb +32 -0
  56. data/lib/pakyow/presenter/views/page.rb +72 -0
  57. data/lib/pakyow/presenter/views/partial.rb +28 -0
  58. data/lib/pakyow/presenter.rb +33 -0
  59. data/lib/pakyow/views/errors/layouts/development_error.html +102 -0
  60. data/lib/pakyow/views/errors/layouts/production_error.html +27 -0
  61. data/lib/pakyow/views/errors/pages/404.html +12 -0
  62. data/lib/pakyow/views/errors/pages/500.html +12 -0
  63. data/lib/pakyow/views/errors/pages/development/500.html +39 -0
  64. data/lib/string_doc/attributes.rb +109 -0
  65. data/lib/string_doc/meta_attributes.rb +48 -0
  66. data/lib/string_doc/meta_node.rb +328 -0
  67. data/lib/string_doc/node.rb +377 -0
  68. data/lib/string_doc.rb +642 -0
  69. metadata +95 -75
  70. data/pakyow-presenter/LICENSE +0 -20
  71. data/pakyow-presenter/lib/pakyow/presenter/attributes.rb +0 -228
  72. data/pakyow-presenter/lib/pakyow/presenter/base.rb +0 -38
  73. data/pakyow-presenter/lib/pakyow/presenter/binder.rb +0 -116
  74. data/pakyow-presenter/lib/pakyow/presenter/binder_set.rb +0 -94
  75. data/pakyow-presenter/lib/pakyow/presenter/binding_eval.rb +0 -37
  76. data/pakyow-presenter/lib/pakyow/presenter/config/presenter.rb +0 -42
  77. data/pakyow-presenter/lib/pakyow/presenter/container.rb +0 -6
  78. data/pakyow-presenter/lib/pakyow/presenter/doc_helpers.rb +0 -17
  79. data/pakyow-presenter/lib/pakyow/presenter/exceptions.rb +0 -11
  80. data/pakyow-presenter/lib/pakyow/presenter/ext/app.rb +0 -33
  81. data/pakyow-presenter/lib/pakyow/presenter/ext/call_context.rb +0 -28
  82. data/pakyow-presenter/lib/pakyow/presenter/helpers.rb +0 -46
  83. data/pakyow-presenter/lib/pakyow/presenter/page.rb +0 -110
  84. data/pakyow-presenter/lib/pakyow/presenter/partial.rb +0 -6
  85. data/pakyow-presenter/lib/pakyow/presenter/presenter.rb +0 -232
  86. data/pakyow-presenter/lib/pakyow/presenter/string_doc.rb +0 -380
  87. data/pakyow-presenter/lib/pakyow/presenter/string_doc_parser.rb +0 -144
  88. data/pakyow-presenter/lib/pakyow/presenter/string_doc_renderer.rb +0 -18
  89. data/pakyow-presenter/lib/pakyow/presenter/template.rb +0 -51
  90. data/pakyow-presenter/lib/pakyow/presenter/view.rb +0 -541
  91. data/pakyow-presenter/lib/pakyow/presenter/view_collection.rb +0 -330
  92. data/pakyow-presenter/lib/pakyow/presenter/view_composer.rb +0 -237
  93. data/pakyow-presenter/lib/pakyow/presenter/view_context.rb +0 -111
  94. data/pakyow-presenter/lib/pakyow/presenter/view_store.rb +0 -262
  95. data/pakyow-presenter/lib/pakyow/presenter/view_store_loader.rb +0 -43
  96. data/pakyow-presenter/lib/pakyow/presenter/view_version.rb +0 -113
  97. data/pakyow-presenter/lib/pakyow/presenter.rb +0 -8
  98. data/pakyow-presenter/lib/pakyow/views/errors/404.erb +0 -26
  99. data/pakyow-presenter/lib/pakyow/views/errors/500.erb +0 -23
  100. data/pakyow-presenter/lib/pakyow-presenter.rb +0 -1
@@ -1,541 +0,0 @@
1
- require 'forwardable'
2
-
3
- module Pakyow
4
- module Presenter
5
- class View
6
- extend Forwardable
7
-
8
- def_delegators :@doc, :title=, :title, :remove, :clear, :text, :html, :exists?
9
-
10
- # The object responsible for parsing, manipulating, and rendering
11
- # the underlying HTML document for the view.
12
- #
13
- attr_reader :doc
14
-
15
- # The scope, if any, that the view belongs to.
16
- #
17
- attr_accessor :scoped_as
18
-
19
- # Creates a view, running `contents` through any registered view processors for `format`.
20
- #
21
- # @param contents [String] the contents of the view
22
- # @param format [Symbol] the format of contents
23
- #
24
- def initialize(contents = '', format: :html)
25
- @doc = Config.presenter.view_doc_class.new(Presenter.process(contents, format))
26
- end
27
-
28
- def initialize_copy(original_view)
29
- super
30
-
31
- @doc = original_view.doc.dup
32
- @scoped_as = original_view.scoped_as
33
- end
34
-
35
- # Creates a new view with a soft copy of doc.
36
- #
37
- def soft_copy
38
- copy = View.from_doc(@doc.soft_copy)
39
- copy.scoped_as = scoped_as
40
- copy
41
- end
42
-
43
- # Creates a view from a doc.
44
- #
45
- # @see StringDoc
46
- #
47
- def self.from_doc(doc)
48
- view = new
49
- view.instance_variable_set(:@doc, doc)
50
- view
51
- end
52
-
53
- # Creates a view from a file.
54
- #
55
- def self.load(path)
56
- new(File.read(path), format: File.format(path))
57
- end
58
-
59
- def ==(other)
60
- self.class == other.class && @doc == other.doc
61
- end
62
-
63
- # Allows multiple attributes to be set at once.
64
- #
65
- # view.attrs(class: '...', style: '...')
66
- #
67
- def attrs(attrs = {})
68
- return Attributes.new(@doc) if attrs.empty?
69
- bind_attributes_to_doc(attrs, @doc)
70
- end
71
-
72
- def text=(text)
73
- text = text.call(self.text) if text.is_a?(Proc)
74
- @doc.text = text
75
- end
76
-
77
- def html=(html)
78
- html = html.call(self.html) if html.is_a?(Proc)
79
- @doc.html = html
80
- end
81
-
82
- def append(view)
83
- @doc.append(view.doc)
84
- end
85
-
86
- def prepend(view)
87
- @doc.prepend(view.doc)
88
- end
89
-
90
- def after(view)
91
- @doc.after(view.doc)
92
- end
93
-
94
- def before(view)
95
- @doc.before(view.doc)
96
- end
97
-
98
- def replace(view)
99
- replacement = view.is_a?(View) ? view.doc : view
100
- @doc.replace(replacement)
101
- end
102
-
103
- def scope(name)
104
- name = name.to_sym
105
- @doc.scope(name).inject(ViewCollection.new(name)) do |coll, scope|
106
- view = View.from_doc(scope[:doc])
107
- view.scoped_as = name
108
- coll << view
109
- end
110
- end
111
-
112
- def prop(name)
113
- name = name.to_sym
114
- @doc.prop(scoped_as, name).inject(ViewCollection.new(scoped_as)) do |coll, prop|
115
- view = View.from_doc(prop[:doc])
116
- view.scoped_as = scoped_as
117
- coll << view
118
- end
119
- end
120
-
121
- def version
122
- return unless versioned?
123
- @doc.get_attribute(:'data-version').to_sym
124
- end
125
-
126
- def versioned?
127
- !@doc.get_attribute(:'data-version').nil?
128
- end
129
-
130
- def component(name)
131
- name = name.to_sym
132
- @doc.component(name).inject(ViewCollection.new(scoped_as)) do |coll, component|
133
- view = View.from_doc(component[:doc])
134
- view.scoped_as = scoped_as
135
- coll << view
136
- end
137
- end
138
-
139
- # call-seq:
140
- # with {|view| block}
141
- #
142
- # Creates a context in which view manipulations can be performed.
143
- #
144
- def with(&block)
145
- if block.arity == 0
146
- instance_exec(&block)
147
- else
148
- yield(self)
149
- end
150
-
151
- self
152
- end
153
-
154
- # call-seq:
155
- # for {|view, datum| block}
156
- #
157
- # Yields a view and its matching dataum. This is driven by the view,
158
- # meaning datums are yielded until no more views are available. For
159
- # the single View case, only one view/datum pair is yielded.
160
- #
161
- # (this is basically Bret's `map` function)
162
- #
163
- def for(data, &block)
164
- datum = Array.ensure(data).first
165
- if block.arity == 1
166
- instance_exec(datum, &block)
167
- else
168
- block.call(self, datum)
169
- end
170
- end
171
-
172
- # call-seq:
173
- # for_with_index {|view, datum, i| block}
174
- #
175
- # Yields a view, its matching dataum, and the index. See #for.
176
- #
177
- def for_with_index(data, &block)
178
- self.for(data) do |ctx, datum|
179
- if block.arity == 2
180
- ctx.instance_exec(datum, 0, &block)
181
- else
182
- block.call(ctx, datum, 0)
183
- end
184
- end
185
- end
186
-
187
- # call-seq:
188
- # match(data) => ViewCollection
189
- #
190
- # Returns a ViewCollection object that has been manipulated to match the data.
191
- # For the single View case, the ViewCollection collection will consist n copies
192
- # of self, where n = data.length.
193
- #
194
- def match(data)
195
- data = Array.ensure(data)
196
- coll = ViewCollection.new(scoped_as)
197
-
198
- # an empty set always means an empty view
199
- if data.empty?
200
- remove
201
- else
202
- # the original view match the first datum
203
- coll << self
204
-
205
- working = self
206
-
207
- # create views for the other datums
208
- data[1..-1].inject(coll) { |coll|
209
- duped_view = working.soft_copy
210
- working.after(duped_view)
211
- working = duped_view
212
- coll << duped_view
213
- }
214
- end
215
-
216
- # return the new collection
217
- coll
218
- end
219
-
220
- # call-seq:
221
- # repeat(data) {|view, datum| block}
222
- #
223
- # Matches self with data and yields a view/datum pair.
224
- #
225
- def repeat(data, &block)
226
- match(data).for(data, &block)
227
- end
228
-
229
- # call-seq:
230
- # repeat_with_index(data) {|view, datum, i| block}
231
- #
232
- # Matches self with data and yields a view/datum pair with index.
233
- #
234
- def repeat_with_index(data, &block)
235
- match(data).for_with_index(data, &block)
236
- end
237
-
238
- # call-seq:
239
- # bind(data)
240
- #
241
- # Binds a single datum across existing scopes.
242
- #
243
- def bind(data, bindings: {}, context: nil, &block)
244
- datum = Array.ensure(data).first
245
- bind_data_to_scope(datum, doc.scopes.first, bindings, context)
246
-
247
- id = nil
248
- if data.is_a?(Hash)
249
- id = data[:id]
250
- elsif data.respond_to?(:id)
251
- id = data.id
252
- end
253
-
254
- attrs.send(:'data-id=', data[:id]) unless id.nil?
255
- return if block.nil?
256
-
257
- if block.arity == 1
258
- instance_exec(datum, &block)
259
- else
260
- block.call(self, datum)
261
- end
262
-
263
- self
264
- end
265
-
266
- # call-seq:
267
- # bind_with_index(data)
268
- #
269
- # Binds data across existing scopes, yielding a view/datum pair with index.
270
- #
271
- def bind_with_index(*a, **k, &block)
272
- bind(*a, **k) do |ctx, datum|
273
- if block.arity == 2
274
- ctx.instance_exec(datum, 0, &block)
275
- else
276
- block.call(ctx, datum, 0)
277
- end
278
- end
279
- end
280
-
281
- # call-seq:
282
- # apply(data)
283
- #
284
- # Matches self to data then binds data to the view.
285
- #
286
- def apply(data, bindings: {}, context: nil, &block)
287
- match(data).bind(data, bindings: bindings, context: context, &block)
288
- end
289
-
290
- def includes(partial_map)
291
- doc_partials = @doc.partials
292
- partial_map = partial_map.dup
293
-
294
- # mixin all the partials
295
- doc_partials.each do |partial_name, partial_docs|
296
- partials = Array.ensure(partial_map[partial_name])
297
-
298
- partial_docs.each_with_index do |partial_doc, i|
299
- replacement = partials[i]
300
- next if replacement.nil?
301
-
302
- if replacement.is_a?(ViewCollection)
303
- partial_doc.replace(replacement.views.first.doc.dup)
304
- partials = replacement.views
305
- else
306
- partial_doc.replace(replacement.doc)
307
- end
308
- end
309
- end
310
-
311
- # refind the partials
312
- doc_partials = @doc.partials
313
-
314
- # if mixed in partials included partials, we want to run includes again with a new map
315
- if doc_partials.count > 0 && (partial_map.keys - doc_partials.keys).count < partial_map.keys.count
316
- includes(partial_map)
317
- end
318
-
319
- self
320
- end
321
-
322
- def to_html
323
- @doc.to_html
324
- end
325
- alias :to_s :to_html
326
-
327
- def component?
328
- !attrs.send(:'data-ui').value.empty?
329
- end
330
-
331
- def component_name
332
- return unless component?
333
- attrs.send(:'data-ui').value
334
- end
335
-
336
- # Convenience method for parity with Presenter::ViewCollection.
337
- #
338
- def length
339
- 1
340
- end
341
-
342
- # Convenience method for parity with Presenter::ViewCollection.
343
- #
344
- def first
345
- self
346
- end
347
-
348
- private
349
-
350
- def adjust_value_parts(value, parts)
351
- return value unless value.is_a?(Hash)
352
-
353
- parts_to_keep = parts.fetch(:include, value.keys)
354
- parts_to_keep -= parts.fetch(:exclude, [])
355
-
356
- value.keep_if { |part, _| parts_to_keep.include?(part) }
357
- end
358
-
359
- def bind_data_to_scope(data, scope_info, bindings, ctx)
360
- return unless data
361
- return unless scope_info
362
-
363
- scope = scope_info[:scope]
364
- bind_data_to_root(data, scope, bindings, ctx)
365
-
366
- scope_info[:props].each do |prop_info|
367
- catch(:unbound) do
368
- prop = prop_info[:prop]
369
- doc = prop_info[:doc]
370
- parts = prop_info[:parts]
371
-
372
- if DocHelpers.form_field?(doc.tagname)
373
- set_form_field_name(doc, scope, prop)
374
- end
375
-
376
- if data_has_prop?(data, prop) || Binder.instance.has_scoped_prop?(scope, prop, bindings)
377
- value = Binder.instance.value_for_scoped_prop(scope, prop, data, bindings, ctx)
378
- value = adjust_value_parts(value, parts)
379
-
380
- if DocHelpers.form_field?(doc.tagname)
381
- bind_to_form_field(doc, scope, prop, value, data, ctx)
382
- end
383
-
384
- bind_data_to_doc(doc, value)
385
- else
386
- handle_unbound_data(scope, prop)
387
- end
388
- end
389
- end
390
- end
391
-
392
- def bind_data_to_root(data, scope, bindings, ctx)
393
- value = Binder.instance.value_for_scoped_prop(scope, :_root, data, bindings, ctx)
394
- return if value.nil?
395
-
396
- value.is_a?(Hash) ? bind_attributes_to_doc(value, doc) : bind_value_to_doc(value, doc)
397
- end
398
-
399
- def bind_data_to_doc(doc, data)
400
- data.is_a?(Hash) ? bind_attributes_to_doc(data, doc) : bind_value_to_doc(data, doc)
401
- end
402
-
403
- def data_has_prop?(data, prop)
404
- (data.is_a?(Hash) && (data.key?(prop) || data.key?(prop.to_s))) || (!data.is_a?(Hash) && data.class.method_defined?(prop))
405
- end
406
-
407
- def bind_value_to_doc(value, doc)
408
- value = String(value)
409
-
410
- tag = doc.tagname
411
- return if DocHelpers.tag_without_value?(tag)
412
-
413
- if DocHelpers.self_closing_tag?(tag)
414
- # don't override value if set
415
- if !doc.get_attribute(:value) || doc.get_attribute(:value).empty?
416
- doc.set_attribute(:value, value)
417
- end
418
- else
419
- doc.html = value
420
- end
421
- end
422
-
423
- def bind_to_form_field(doc, scope, prop, value, bindable, ctx)
424
- # special binding for checkboxes and radio buttons
425
- if doc.tagname == 'input' && (doc.get_attribute(:type) == 'checkbox' || doc.get_attribute(:type) == 'radio')
426
- bind_to_checked_field(doc, value)
427
- # special binding for selects
428
- elsif doc.tagname == 'select'
429
- bind_to_select_field(doc, scope, prop, value, bindable, ctx)
430
- end
431
- end
432
-
433
- def bind_to_checked_field(doc, value)
434
- if value == true || (doc.get_attribute(:value) && doc.get_attribute(:value) == value.to_s)
435
- doc.set_attribute(:checked, 'checked')
436
- else
437
- doc.remove_attribute(:checked)
438
- end
439
-
440
- # coerce to string since booleans are often used and fail when binding to a view
441
- value.to_s
442
- end
443
-
444
- def bind_to_select_field(doc, scope, prop, value, bindable, ctx)
445
- create_select_options(doc, scope, prop, value, bindable, ctx)
446
- select_option_with_value(doc, value)
447
- end
448
-
449
- def set_form_field_name(doc, scope, prop)
450
- return if doc.get_attribute(:name) && !doc.get_attribute(:name).empty? # don't overwrite the name if already defined
451
- doc.set_attribute(:name, "#{scope}[#{prop}]")
452
- end
453
-
454
- def create_select_options(doc, scope, prop, value, bindable, ctx)
455
- options = Binder.instance.options_for_scoped_prop(scope, prop, bindable, ctx)
456
- return if options.nil?
457
-
458
- nodes = Oga::XML::Document.new
459
-
460
- until options.length == 0
461
- catch :optgroup do
462
- o = options.first
463
-
464
- # an array containing value/content
465
- if o.is_a?(Array)
466
- node = Oga::XML::Element.new(name: 'option')
467
- node.inner_text = o[1].to_s
468
- node.set('value', o[0].to_s)
469
- nodes.children << node
470
- options.shift
471
- else # likely an object (e.g. string); start a group
472
- node_group = Oga::XML::Element.new(name: 'optgroup')
473
- node_group.set('label', o.to_s)
474
- nodes.children << node_group
475
-
476
- options.shift
477
-
478
- options[0..-1].each_with_index { |o2,i2|
479
- # starting a new group
480
- throw :optgroup unless o2.is_a?(Array)
481
-
482
- h.option o2[1].to_s, value: o2[0].to_s
483
-
484
- node = Oga::XML::Element.new(name: 'option')
485
- node.inner_text = o2[1].to_s
486
- node.set('value', o2[0].to_s)
487
- node_group.children << node
488
- options.shift
489
- }
490
- end
491
- end
492
- end
493
-
494
- # remove existing options
495
- doc.clear
496
-
497
- # add generated options
498
- doc.append(nodes.to_xml)
499
- end
500
-
501
- def select_option_with_value(doc, value)
502
- option = doc.option(value: value)
503
- return if option.nil?
504
-
505
- option.set_attribute(:selected, 'selected')
506
- end
507
-
508
- def handle_unbound_data(scope, prop)
509
- Pakyow.logger.warn("Unbound data for #{scope}[#{prop}]") if Pakyow.logger
510
- throw :unbound
511
- end
512
-
513
- def bind_attributes_to_doc(attrs, doc)
514
- attrs.each do |attr, v|
515
- case attr
516
- when :content
517
- v = v.to_proc.call(doc.html) if v.respond_to?(:to_proc)
518
- bind_value_to_doc(v, doc)
519
- when :view
520
- v.call(View.from_doc(doc))
521
- else
522
- attr = attr.to_s
523
- attrs = Attributes.new(doc)
524
-
525
- if v.respond_to?(:to_proc)
526
- attribute = attrs.send(attr)
527
- ret = v.to_proc.call(attribute)
528
- value = ret.respond_to?(:value) ? ret.value : ret
529
-
530
- attrs.send("#{attr}=", value)
531
- elsif v.nil?
532
- doc.remove_attribute(attr)
533
- else
534
- attrs.send("#{attr}=", v)
535
- end
536
- end
537
- end
538
- end
539
- end
540
- end
541
- end