pakyow-presenter 0.9.1 → 0.10.0
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/pakyow-presenter/CHANGELOG.md +94 -0
- data/pakyow-presenter/{MIT-LICENSE → LICENSE} +2 -2
- data/pakyow-presenter/README.md +36 -0
- data/pakyow-presenter/lib/pakyow-presenter.rb +1 -4
- data/pakyow-presenter/lib/presenter/attributes.rb +10 -1
- data/pakyow-presenter/lib/presenter/base.rb +2 -1
- data/pakyow-presenter/lib/presenter/binder.rb +20 -6
- data/pakyow-presenter/lib/presenter/binder_set.rb +21 -17
- data/pakyow-presenter/lib/presenter/config/presenter.rb +2 -2
- data/pakyow-presenter/lib/presenter/ext/app.rb +32 -1
- data/pakyow-presenter/lib/presenter/helpers.rb +6 -6
- data/pakyow-presenter/lib/presenter/page.rb +4 -4
- data/pakyow-presenter/lib/presenter/presenter.rb +32 -10
- data/pakyow-presenter/lib/presenter/string_doc.rb +78 -11
- data/pakyow-presenter/lib/presenter/string_doc_parser.rb +16 -7
- data/pakyow-presenter/lib/presenter/view.rb +55 -17
- data/pakyow-presenter/lib/presenter/view_collection.rb +74 -7
- data/pakyow-presenter/lib/presenter/view_composer.rb +52 -8
- data/pakyow-presenter/lib/presenter/view_context.rb +19 -1
- data/pakyow-presenter/lib/presenter/view_store.rb +202 -163
- data/pakyow-presenter/lib/presenter/view_store_loader.rb +43 -0
- data/pakyow-presenter/lib/presenter/view_version.rb +97 -0
- data/pakyow-presenter/lib/views/errors/404.html +5 -0
- data/pakyow-presenter/{README → lib/views/errors/500.html} +0 -0
- metadata +36 -21
- data/pakyow-presenter/CHANGES +0 -57
- data/pakyow-presenter/lib/presenter/nokogiri_doc.rb +0 -321
@@ -32,11 +32,11 @@ module Pakyow
|
|
32
32
|
|
33
33
|
children = node.children.reject {|n| n.is_a?(Nokogiri::XML::Text)}
|
34
34
|
attributes = node.attributes
|
35
|
-
if children.empty? && !significant?(node)
|
35
|
+
if !structure.empty? && children.empty? && !significant?(node)
|
36
36
|
structure << [node.to_html, {}, []]
|
37
37
|
else
|
38
38
|
if significant?(node)
|
39
|
-
if scope?(node) || prop?(node) || option?(node)
|
39
|
+
if scope?(node) || prop?(node) || option?(node) || component?(node)
|
40
40
|
attr_structure = attributes.inject({}) do |attrs, attr|
|
41
41
|
attrs[attr[1].name.to_sym] = attr[1].value
|
42
42
|
attrs
|
@@ -55,10 +55,14 @@ module Pakyow
|
|
55
55
|
structure << [node.to_html, { partial: name }, []]
|
56
56
|
end
|
57
57
|
else
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
if node.is_a?(Nokogiri::XML::Text)
|
59
|
+
structure << [node.text, {}, []]
|
60
|
+
else
|
61
|
+
attr_s = attributes.inject('') { |s, a| s << " #{a[1].name}=\"#{a[1].value}\""; s }
|
62
|
+
closing = [['>', {}, parse(node)]]
|
63
|
+
closing << ['</' + node.name + '>', {}, []] unless self_closing?(node.name)
|
64
|
+
structure << ['<' + node.name + attr_s, {}, closing]
|
65
|
+
end
|
62
66
|
end
|
63
67
|
end
|
64
68
|
end
|
@@ -67,7 +71,7 @@ module Pakyow
|
|
67
71
|
end
|
68
72
|
|
69
73
|
def significant?(node)
|
70
|
-
scope?(node) || prop?(node) || container?(node) || partial?(node) || option?(node)
|
74
|
+
scope?(node) || prop?(node) || container?(node) || partial?(node) || option?(node) || component?(node)
|
71
75
|
end
|
72
76
|
|
73
77
|
def scope?(node)
|
@@ -96,6 +100,11 @@ module Pakyow
|
|
96
100
|
node.name == 'option'
|
97
101
|
end
|
98
102
|
|
103
|
+
def component?(node)
|
104
|
+
return false unless node['data-ui']
|
105
|
+
return true
|
106
|
+
end
|
107
|
+
|
99
108
|
def breadth_first(doc)
|
100
109
|
queue = [doc]
|
101
110
|
until queue.empty?
|
@@ -5,7 +5,7 @@ module Pakyow
|
|
5
5
|
class View
|
6
6
|
extend Forwardable
|
7
7
|
|
8
|
-
def_delegators :@doc, :title=, :title, :remove, :clear, :text, :html
|
8
|
+
def_delegators :@doc, :title=, :title, :remove, :clear, :text, :html, :exists?
|
9
9
|
|
10
10
|
# The object responsible for parsing, manipulating, and rendering
|
11
11
|
# the underlying HTML document for the view.
|
@@ -103,7 +103,7 @@ module Pakyow
|
|
103
103
|
|
104
104
|
def scope(name)
|
105
105
|
name = name.to_sym
|
106
|
-
@doc.scope(name).inject(ViewCollection.new) do |coll, scope|
|
106
|
+
@doc.scope(name).inject(ViewCollection.new(name)) do |coll, scope|
|
107
107
|
view = View.from_doc(scope[:doc])
|
108
108
|
view.scoped_as = name
|
109
109
|
coll << view
|
@@ -112,13 +112,31 @@ module Pakyow
|
|
112
112
|
|
113
113
|
def prop(name)
|
114
114
|
name = name.to_sym
|
115
|
-
@doc.prop(scoped_as, name).inject(ViewCollection.new) do |coll, prop|
|
115
|
+
@doc.prop(scoped_as, name).inject(ViewCollection.new(scoped_as)) do |coll, prop|
|
116
116
|
view = View.from_doc(prop[:doc])
|
117
117
|
view.scoped_as = scoped_as
|
118
118
|
coll << view
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
def version
|
123
|
+
return unless versioned?
|
124
|
+
@doc.get_attribute(:'data-version').to_sym
|
125
|
+
end
|
126
|
+
|
127
|
+
def versioned?
|
128
|
+
!@doc.get_attribute(:'data-version').nil?
|
129
|
+
end
|
130
|
+
|
131
|
+
def component(name)
|
132
|
+
name = name.to_sym
|
133
|
+
@doc.component(name).inject(ViewCollection.new(scoped_as)) do |coll, component|
|
134
|
+
view = View.from_doc(component[:doc])
|
135
|
+
view.scoped_as = scoped_as
|
136
|
+
coll << view
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
122
140
|
# call-seq:
|
123
141
|
# with {|view| block}
|
124
142
|
#
|
@@ -176,22 +194,22 @@ module Pakyow
|
|
176
194
|
#
|
177
195
|
def match(data)
|
178
196
|
data = Array.ensure(data)
|
179
|
-
coll = ViewCollection.new
|
197
|
+
coll = ViewCollection.new(scoped_as)
|
180
198
|
|
181
199
|
# an empty set always means an empty view
|
182
200
|
if data.empty?
|
183
201
|
remove
|
184
202
|
else
|
185
|
-
# dup for later
|
186
|
-
original_view = dup if data.length > 1
|
187
|
-
|
188
203
|
# the original view match the first datum
|
189
204
|
coll << self
|
190
205
|
|
206
|
+
working = self
|
207
|
+
|
191
208
|
# create views for the other datums
|
192
209
|
data[1..-1].inject(coll) { |coll|
|
193
|
-
duped_view =
|
194
|
-
after(duped_view)
|
210
|
+
duped_view = working.soft_copy
|
211
|
+
working.after(duped_view)
|
212
|
+
working = duped_view
|
195
213
|
coll << duped_view
|
196
214
|
}
|
197
215
|
end
|
@@ -226,6 +244,15 @@ module Pakyow
|
|
226
244
|
def bind(data, bindings: {}, context: nil, &block)
|
227
245
|
datum = Array.ensure(data).first
|
228
246
|
bind_data_to_scope(datum, doc.scopes.first, bindings, context)
|
247
|
+
|
248
|
+
id = nil
|
249
|
+
if data.is_a?(Hash)
|
250
|
+
id = data[:id]
|
251
|
+
elsif data.respond_to?(:id)
|
252
|
+
id = data.id
|
253
|
+
end
|
254
|
+
|
255
|
+
attrs.send(:'data-id=', data[:id]) unless id.nil?
|
229
256
|
return if block.nil?
|
230
257
|
|
231
258
|
if block.arity == 1
|
@@ -233,6 +260,8 @@ module Pakyow
|
|
233
260
|
else
|
234
261
|
block.call(self, datum)
|
235
262
|
end
|
263
|
+
|
264
|
+
self
|
236
265
|
end
|
237
266
|
|
238
267
|
# call-seq:
|
@@ -286,6 +315,15 @@ module Pakyow
|
|
286
315
|
end
|
287
316
|
alias :to_s :to_html
|
288
317
|
|
318
|
+
def component?
|
319
|
+
!attrs.send(:'data-ui').value.empty?
|
320
|
+
end
|
321
|
+
|
322
|
+
def component_name
|
323
|
+
return unless component?
|
324
|
+
attrs.send(:'data-ui').value
|
325
|
+
end
|
326
|
+
|
289
327
|
private
|
290
328
|
|
291
329
|
def bind_data_to_scope(data, scope_info, bindings, ctx)
|
@@ -305,7 +343,7 @@ module Pakyow
|
|
305
343
|
end
|
306
344
|
|
307
345
|
if data_has_prop?(data, prop) || Binder.instance.has_scoped_prop?(scope, prop, bindings)
|
308
|
-
value = Binder.instance.value_for_scoped_prop(scope, prop, data, bindings, ctx)
|
346
|
+
value = Binder.instance.value_for_scoped_prop(scope, prop, data, bindings, ctx)
|
309
347
|
|
310
348
|
if DocHelpers.form_field?(doc.tagname)
|
311
349
|
bind_to_form_field(doc, scope, prop, value, data, ctx)
|
@@ -436,18 +474,18 @@ module Pakyow
|
|
436
474
|
attrs.each do |attr, v|
|
437
475
|
case attr
|
438
476
|
when :content
|
439
|
-
v = v.call(doc.
|
477
|
+
v = v.to_proc.call(doc.html) if v.respond_to?(:to_proc)
|
440
478
|
bind_value_to_doc(v, doc)
|
441
|
-
next
|
442
479
|
when :view
|
443
|
-
v.call(
|
444
|
-
next
|
480
|
+
v.call(View.from_doc(doc))
|
445
481
|
else
|
446
|
-
attr
|
482
|
+
attr = attr.to_s
|
447
483
|
attrs = Attributes.new(doc)
|
448
|
-
v = v.call(attrs.send(attr)) if v.is_a?(Proc)
|
449
484
|
|
450
|
-
if v.
|
485
|
+
if v.respond_to?(:to_proc)
|
486
|
+
# Evaluating the proc will set the value in the doc
|
487
|
+
v.to_proc.call(attrs.send(attr))
|
488
|
+
elsif v.nil?
|
451
489
|
doc.remove_attribute(attr)
|
452
490
|
else
|
453
491
|
attrs.send(:"#{attr}=", v)
|
@@ -3,8 +3,19 @@ module Pakyow
|
|
3
3
|
class ViewCollection
|
4
4
|
include Enumerable
|
5
5
|
|
6
|
-
|
6
|
+
attr_reader :views, :scoped_as
|
7
|
+
|
8
|
+
def initialize(scope = nil)
|
7
9
|
@views = []
|
10
|
+
@scoped_as = scope
|
11
|
+
end
|
12
|
+
|
13
|
+
def ==(other)
|
14
|
+
@views.each_with_index do |view, i|
|
15
|
+
return false if view != other.views[i]
|
16
|
+
end
|
17
|
+
|
18
|
+
return true
|
8
19
|
end
|
9
20
|
|
10
21
|
def each
|
@@ -80,7 +91,7 @@ module Pakyow
|
|
80
91
|
end
|
81
92
|
|
82
93
|
def scope(name)
|
83
|
-
inject(ViewCollection.new) { |coll, view|
|
94
|
+
collection = inject(ViewCollection.new(name)) { |coll, view|
|
84
95
|
scopes = view.scope(name)
|
85
96
|
next if scopes.nil?
|
86
97
|
|
@@ -88,10 +99,16 @@ module Pakyow
|
|
88
99
|
coll << scoped_view
|
89
100
|
}
|
90
101
|
}
|
102
|
+
|
103
|
+
if collection.versioned?
|
104
|
+
ViewVersion.new(collection.views)
|
105
|
+
else
|
106
|
+
collection
|
107
|
+
end
|
91
108
|
end
|
92
109
|
|
93
110
|
def prop(name)
|
94
|
-
inject(ViewCollection.new) { |coll, view|
|
111
|
+
inject(ViewCollection.new(scoped_as)) { |coll, view|
|
95
112
|
scopes = view.prop(name)
|
96
113
|
next if scopes.nil?
|
97
114
|
|
@@ -101,6 +118,53 @@ module Pakyow
|
|
101
118
|
}
|
102
119
|
end
|
103
120
|
|
121
|
+
def versioned?
|
122
|
+
each do |view|
|
123
|
+
return true if view.versioned?
|
124
|
+
end
|
125
|
+
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def exists?
|
130
|
+
each do |view|
|
131
|
+
return true if view.exists?
|
132
|
+
end
|
133
|
+
|
134
|
+
false
|
135
|
+
end
|
136
|
+
|
137
|
+
def component(name)
|
138
|
+
collection = inject(ViewCollection.new(scoped_as)) { |coll, view|
|
139
|
+
scopes = view.component(name)
|
140
|
+
next if scopes.nil?
|
141
|
+
|
142
|
+
scopes.inject(coll) { |coll, scoped_view|
|
143
|
+
coll << scoped_view
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
if collection.versioned?
|
148
|
+
ViewVersion.new(collection.views)
|
149
|
+
else
|
150
|
+
collection
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def component?
|
155
|
+
each do |view|
|
156
|
+
return true if view.component?
|
157
|
+
end
|
158
|
+
|
159
|
+
false
|
160
|
+
end
|
161
|
+
|
162
|
+
def component_name
|
163
|
+
each do |view|
|
164
|
+
return view.component_name if view.component?
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
104
168
|
# call-seq:
|
105
169
|
# with {|view| block}
|
106
170
|
#
|
@@ -137,6 +201,8 @@ module Pakyow
|
|
137
201
|
block.call(view, data[i])
|
138
202
|
end
|
139
203
|
end
|
204
|
+
|
205
|
+
self
|
140
206
|
end
|
141
207
|
|
142
208
|
# call-seq:
|
@@ -166,22 +232,23 @@ module Pakyow
|
|
166
232
|
# will consist n copies of self[data index] || self[-1], where n = data.length.
|
167
233
|
#
|
168
234
|
def match(data)
|
235
|
+
return self if length == 0
|
169
236
|
data = Array.ensure(data)
|
170
237
|
|
171
238
|
# an empty set always means an empty view
|
172
239
|
if data.empty?
|
173
240
|
remove
|
174
241
|
else
|
175
|
-
original_view = self[-1].soft_copy if data.length > length
|
176
|
-
|
177
242
|
if length > data.length
|
178
243
|
self[data.length..-1].each do |view|
|
179
244
|
view.remove
|
180
245
|
end
|
181
246
|
else
|
247
|
+
working = self[-1]
|
182
248
|
data[length..-1].each do
|
183
|
-
duped_view =
|
184
|
-
|
249
|
+
duped_view = working.soft_copy
|
250
|
+
working.after(duped_view)
|
251
|
+
working = duped_view
|
185
252
|
self << duped_view
|
186
253
|
end
|
187
254
|
end
|
@@ -10,7 +10,8 @@ module Pakyow
|
|
10
10
|
extend Forwardable
|
11
11
|
|
12
12
|
def_delegators :template, :title, :title=
|
13
|
-
def_delegators :parts, :
|
13
|
+
def_delegators :parts, :prop, :component
|
14
|
+
def_delegators :view, :to_html
|
14
15
|
|
15
16
|
attr_reader :store, :path, :page, :partials
|
16
17
|
|
@@ -72,7 +73,7 @@ module Pakyow
|
|
72
73
|
def template=(template)
|
73
74
|
unless template.is_a?(Template)
|
74
75
|
# get template by name
|
75
|
-
template = @store.template(template)
|
76
|
+
template = @store.template(template.to_sym)
|
76
77
|
end
|
77
78
|
|
78
79
|
@template = template
|
@@ -110,17 +111,43 @@ module Pakyow
|
|
110
111
|
end
|
111
112
|
|
112
113
|
def parts
|
114
|
+
# create an array to hold the parts
|
113
115
|
parts = ViewCollection.new
|
116
|
+
|
117
|
+
# add the current template
|
114
118
|
parts << @template
|
115
|
-
@page.each_container { |name, container| parts << container }
|
116
119
|
|
117
|
-
#
|
118
|
-
|
119
|
-
|
120
|
-
|
120
|
+
# add each page container
|
121
|
+
@page.each_container do |_, container|
|
122
|
+
parts << container
|
123
|
+
end
|
124
|
+
|
125
|
+
parts.concat(partials_for_parts(parts))
|
126
|
+
|
121
127
|
return parts
|
122
128
|
end
|
123
129
|
|
130
|
+
def scope(name)
|
131
|
+
collection = parts.scope(name)
|
132
|
+
|
133
|
+
if collection.is_a?(ViewVersion)
|
134
|
+
collection = collection.versions.inject(ViewCollection.new) { |c, v| c << v; c }
|
135
|
+
end
|
136
|
+
|
137
|
+
# include partials so nested scopes/props can be bound to
|
138
|
+
collection.each do |view|
|
139
|
+
view.includes(partials)
|
140
|
+
end
|
141
|
+
|
142
|
+
#TODO make sure anytime we return a collection it tries to version
|
143
|
+
# make this a class level helper method on ViewVersion
|
144
|
+
if !collection.is_a?(ViewVersion) && collection.versioned?
|
145
|
+
ViewVersion.new(collection.views)
|
146
|
+
else
|
147
|
+
collection
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
124
151
|
private
|
125
152
|
|
126
153
|
def build_view
|
@@ -141,13 +168,30 @@ module Pakyow
|
|
141
168
|
if partial_or_path.is_a?(Partial)
|
142
169
|
partial = partial_or_path
|
143
170
|
else
|
144
|
-
partial = Partial.load(@store.expand_partial_path(
|
171
|
+
partial = Partial.load(@store.expand_partial_path(partial_or_path))
|
145
172
|
end
|
146
173
|
|
147
174
|
[name, partial]
|
148
175
|
}]
|
149
176
|
end
|
150
177
|
|
178
|
+
def partials_for_parts(parts, acc = [])
|
179
|
+
# determine the partials to be included
|
180
|
+
available_partials = parts.inject([]) { |sum, part|
|
181
|
+
sum.concat(part.doc.partials.keys)
|
182
|
+
}
|
183
|
+
|
184
|
+
# add available partials as parts
|
185
|
+
partials.select { |name|
|
186
|
+
available_partials.include?(name)
|
187
|
+
}.each_pair { |_, partial|
|
188
|
+
acc << partial
|
189
|
+
partials_for_parts([partial], acc)
|
190
|
+
}
|
191
|
+
|
192
|
+
return acc
|
193
|
+
end
|
194
|
+
|
151
195
|
end
|
152
196
|
end
|
153
197
|
end
|
@@ -6,7 +6,7 @@ module Pakyow
|
|
6
6
|
#
|
7
7
|
class ViewContext
|
8
8
|
include Helpers
|
9
|
-
VIEW_CLASSES = [View, ViewCollection, Partial, Template, Container]
|
9
|
+
VIEW_CLASSES = [View, ViewCollection, Partial, Template, Container, ViewVersion]
|
10
10
|
|
11
11
|
# The arities of misc view methods that switch the behavior from
|
12
12
|
# instance_exec to yield.
|
@@ -14,11 +14,17 @@ module Pakyow
|
|
14
14
|
EXEC_ARITIES = { with: 0, for: 1, for_with_index: 2, repeat: 1,
|
15
15
|
repeat_with_index: 2, bind: 1, bind_with_index: 2, apply: 1 }
|
16
16
|
|
17
|
+
attr_reader :context
|
18
|
+
|
17
19
|
def initialize(view, context)
|
18
20
|
@view = view
|
19
21
|
@context = context
|
20
22
|
end
|
21
23
|
|
24
|
+
def subject
|
25
|
+
@view
|
26
|
+
end
|
27
|
+
|
22
28
|
# View methods that expect context, so it can be mixed in.
|
23
29
|
#
|
24
30
|
%i[bind bind_with_index apply].each do |method|
|
@@ -38,6 +44,18 @@ module Pakyow
|
|
38
44
|
end
|
39
45
|
end
|
40
46
|
|
47
|
+
def scope(name)
|
48
|
+
collection = @view.scope(name)
|
49
|
+
|
50
|
+
if !collection.is_a?(ViewVersion) && collection.versioned?
|
51
|
+
ret = ViewVersion.new(collection.views)
|
52
|
+
else
|
53
|
+
ret = collection
|
54
|
+
end
|
55
|
+
|
56
|
+
handle_return_value(ret)
|
57
|
+
end
|
58
|
+
|
41
59
|
# Pass these through, handling the return value.
|
42
60
|
#
|
43
61
|
def method_missing(method, *args, &block)
|