pakyow-presenter 0.8rc1 → 0.8.rc4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/pakyow-presenter/lib/pakyow-presenter.rb +2 -1
- data/pakyow-presenter/lib/presenter/attributes.rb +128 -35
- data/pakyow-presenter/lib/presenter/base.rb +8 -4
- data/pakyow-presenter/lib/presenter/binder.rb +62 -30
- data/pakyow-presenter/lib/presenter/binder_set.rb +95 -0
- data/pakyow-presenter/lib/presenter/config/presenter.rb +61 -0
- data/pakyow-presenter/lib/presenter/doc_helpers.rb +70 -0
- data/pakyow-presenter/lib/presenter/exceptions.rb +7 -0
- data/pakyow-presenter/lib/presenter/ext/app.rb +32 -0
- data/pakyow-presenter/lib/presenter/helpers.rb +4 -5
- data/pakyow-presenter/lib/presenter/page.rb +126 -0
- data/pakyow-presenter/lib/presenter/partial.rb +24 -0
- data/pakyow-presenter/lib/presenter/presenter.rb +120 -187
- data/pakyow-presenter/lib/presenter/template.rb +79 -0
- data/pakyow-presenter/lib/presenter/view.rb +255 -300
- data/pakyow-presenter/lib/presenter/view_collection.rb +55 -39
- data/pakyow-presenter/lib/presenter/view_store.rb +174 -0
- metadata +43 -32
- data/pakyow-presenter/lib/presenter/bindings.rb +0 -103
- data/pakyow-presenter/lib/presenter/configuration/base.rb +0 -12
- data/pakyow-presenter/lib/presenter/configuration/presenter.rb +0 -45
- data/pakyow-presenter/lib/presenter/lazy_view.rb +0 -42
- data/pakyow-presenter/lib/presenter/view_context.rb +0 -20
- data/pakyow-presenter/lib/presenter/view_lookup_store.rb +0 -220
@@ -0,0 +1,126 @@
|
|
1
|
+
module Pakyow
|
2
|
+
module Presenter
|
3
|
+
class Page
|
4
|
+
MATTER_MATCHER = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def load(path)
|
8
|
+
format = StringUtils.split_at_last_dot(path)[-1]
|
9
|
+
name = File.basename(path, '.*').to_sym
|
10
|
+
contents = FileTest.file?(path) ? File.read(path) : nil
|
11
|
+
|
12
|
+
return Page.new(name, contents, path, format)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :contents, :path
|
17
|
+
|
18
|
+
def initialize(name, contents, path, format = :html)
|
19
|
+
@name, @contents, @path, @format = name, contents, path, format
|
20
|
+
|
21
|
+
@info = {}
|
22
|
+
@content = {}
|
23
|
+
|
24
|
+
unless @contents.nil?
|
25
|
+
parse_info
|
26
|
+
parse_content
|
27
|
+
end
|
28
|
+
|
29
|
+
partials
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize_copy(original_page)
|
33
|
+
super
|
34
|
+
|
35
|
+
# copy content
|
36
|
+
@content = {}
|
37
|
+
original_page.content.each_pair do |k, v|
|
38
|
+
@content[k] = v.dup
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def build(partial_map)
|
43
|
+
partials.each do |container, partial_list|
|
44
|
+
partial_list.each do |partial_name|
|
45
|
+
regex = /<!--\s*@include\s*#{partial_name}\s*-->/
|
46
|
+
@content[container].gsub!(regex, partial_map[partial_name].to_s)
|
47
|
+
|
48
|
+
partial_map.delete(partial_name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# we have more partials
|
53
|
+
if partial_map.count > 0
|
54
|
+
# initiate another build if content contains partials
|
55
|
+
build(partial_map) if partials(true).count > 0
|
56
|
+
end
|
57
|
+
|
58
|
+
return self
|
59
|
+
end
|
60
|
+
|
61
|
+
def content(container = nil)
|
62
|
+
return container.nil? ? @content : @content[container.to_sym]
|
63
|
+
end
|
64
|
+
|
65
|
+
def template
|
66
|
+
@info[:template] || :pakyow
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(page)
|
70
|
+
@contents == page.contents
|
71
|
+
end
|
72
|
+
|
73
|
+
def partials(refind = false)
|
74
|
+
@partials = (!@partials || refind) ? find_partials : @partials
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def parse_info
|
80
|
+
info = parse_front_matter(@contents)
|
81
|
+
info = {} if !info || !info.is_a?(Hash)
|
82
|
+
|
83
|
+
@info = HashUtils.symbolize(info)
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse_content
|
87
|
+
# remove yaml front matter
|
88
|
+
@contents.gsub!(/---(.|\n)*---/, '')
|
89
|
+
|
90
|
+
# process contents
|
91
|
+
@contents = Presenter.process(@contents, @format)
|
92
|
+
|
93
|
+
# find content in named containers
|
94
|
+
within_regex = /<!--\s*@within\s*([a-zA-Z0-9\-_]*)\s*-->(.*?)<!--\s*\/within\s*-->/m
|
95
|
+
|
96
|
+
@contents.scan(within_regex) do |m|
|
97
|
+
container = m[0].to_sym
|
98
|
+
@content[container] = m[1]
|
99
|
+
end
|
100
|
+
|
101
|
+
# find default content
|
102
|
+
@content[:default] = @contents.gsub(within_regex, '')
|
103
|
+
end
|
104
|
+
|
105
|
+
# returns an array of hashes, each with the partial name and doc
|
106
|
+
def find_partials
|
107
|
+
partials = {}
|
108
|
+
|
109
|
+
partial_regex = /<!--\s*@include\s*([a-zA-Z0-9\-_]*)\s*-->/
|
110
|
+
@content.each do |name, content|
|
111
|
+
content.scan(partial_regex) do |m|
|
112
|
+
partials[name] ||= []
|
113
|
+
partials[name] << m[0].to_sym
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
return partials
|
118
|
+
end
|
119
|
+
|
120
|
+
def parse_front_matter(contents)
|
121
|
+
matter = YAML.load(contents.match(MATTER_MATCHER).to_s)
|
122
|
+
return matter
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Pakyow
|
2
|
+
module Presenter
|
3
|
+
class Partial
|
4
|
+
include DocHelpers
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def load(path)
|
8
|
+
format = StringUtils.split_at_last_dot(path)[-1]
|
9
|
+
contents = File.read(path)
|
10
|
+
name = File.basename(path, '.*')[1..-1].to_sym
|
11
|
+
|
12
|
+
return self.new(name, contents, format)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(name, contents, format = :html)
|
17
|
+
@name = name
|
18
|
+
|
19
|
+
processed = Presenter.process(contents, format)
|
20
|
+
@doc = Nokogiri::HTML.fragment(processed)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,255 +1,188 @@
|
|
1
1
|
module Pakyow
|
2
2
|
module Presenter
|
3
|
-
class Presenter
|
3
|
+
class Presenter
|
4
4
|
class << self
|
5
|
-
|
6
|
-
|
5
|
+
def process(contents, format)
|
6
|
+
format = format.to_sym
|
7
7
|
|
8
|
-
|
8
|
+
unless app = Pakyow.app
|
9
|
+
return contents
|
10
|
+
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
12
|
+
unless presenter = app.presenter
|
13
|
+
return contents
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
unless processor = presenter.processor_store[format]
|
17
|
+
Pakyow.logger.warn("No processor defined for extension #{format}") unless format == :html
|
18
|
+
return contents
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
bs
|
21
|
+
return processor.call(contents)
|
22
|
+
end
|
21
23
|
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
25
|
+
Pakyow::App.before(:init) {
|
26
|
+
@presenter = Presenter.new
|
27
|
+
}
|
27
28
|
|
28
|
-
|
29
|
-
@
|
30
|
-
@
|
31
|
-
|
29
|
+
Pakyow::App.after(:match) {
|
30
|
+
@presenter = Pakyow.app.presenter.dup
|
31
|
+
@presenter.prepare_for_request(@request)
|
32
|
+
}
|
32
33
|
|
33
|
-
|
34
|
-
@
|
35
|
-
|
34
|
+
Pakyow::App.after(:route) {
|
35
|
+
if @presenter.presented?
|
36
|
+
@found = true
|
37
|
+
@response.body = [@presenter.content]
|
38
|
+
else
|
39
|
+
@found = false unless found?
|
40
|
+
end
|
41
|
+
}
|
36
42
|
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
Pakyow::App.after(:load) {
|
44
|
+
@presenter.load
|
45
|
+
}
|
40
46
|
|
41
|
-
|
42
|
-
|
47
|
+
Pakyow::App.after(:error) {
|
48
|
+
unless config.app.errors_in_browser
|
49
|
+
@response.body = [@presenter.content] if @presenter.presented?
|
50
|
+
end
|
51
|
+
}
|
52
|
+
|
53
|
+
attr_accessor :processor_store, :binder, :path, :template, :page
|
43
54
|
|
44
|
-
|
45
|
-
|
55
|
+
def initialize
|
56
|
+
setup
|
46
57
|
end
|
47
58
|
|
48
|
-
def
|
49
|
-
|
50
|
-
@
|
59
|
+
def setup
|
60
|
+
@view, @template, @page = nil
|
61
|
+
@constructed = false
|
62
|
+
self.store = :default
|
63
|
+
end
|
51
64
|
|
52
|
-
|
53
|
-
|
65
|
+
def store(name = nil)
|
66
|
+
if name
|
67
|
+
@view_stores[name]
|
54
68
|
else
|
55
|
-
@
|
69
|
+
@view_stores[@store]
|
56
70
|
end
|
57
|
-
@root_path = self.current_view_lookup_store.root_path(@view_path)
|
58
|
-
end
|
59
|
-
|
60
|
-
def reset
|
61
|
-
@request = nil
|
62
|
-
reset_state
|
63
|
-
end
|
64
|
-
|
65
|
-
def presented?
|
66
|
-
#TODO the right thing to do?
|
67
|
-
self.ensure_root_view_built
|
68
|
-
|
69
|
-
@presented
|
70
|
-
end
|
71
|
-
|
72
|
-
def content
|
73
|
-
return unless view
|
74
|
-
request_container = @request.params[:_container] if @request
|
75
|
-
return view.to_html(request_container) if request_container
|
76
|
-
view.to_html(@container_name)
|
77
71
|
end
|
78
72
|
|
79
|
-
|
80
|
-
|
81
|
-
# Some are meant to be called directly and some make up a dsl for dom modification
|
82
|
-
#
|
83
|
-
|
84
|
-
# Call these directly
|
85
|
-
#
|
73
|
+
def store=(name)
|
74
|
+
@store = name
|
86
75
|
|
87
|
-
|
88
|
-
|
89
|
-
klass.new(real_path, is_root_view)
|
76
|
+
return unless @path
|
77
|
+
setup_for_path
|
90
78
|
end
|
91
79
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
80
|
+
def load
|
81
|
+
load_processors
|
82
|
+
load_views
|
83
|
+
load_bindings
|
95
84
|
end
|
96
85
|
|
97
|
-
def
|
98
|
-
|
99
|
-
@root_view
|
100
|
-
end
|
86
|
+
def prepare_for_request(request)
|
87
|
+
@request = request
|
101
88
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
89
|
+
if @request.has_route_vars?
|
90
|
+
@path = StringUtils.remove_route_vars(@request.route_path)
|
91
|
+
else
|
92
|
+
@path = @request.path
|
93
|
+
end
|
107
94
|
|
108
|
-
|
109
|
-
@view_path = nil
|
110
|
-
@root_path = nil
|
95
|
+
setup
|
111
96
|
end
|
112
97
|
|
113
|
-
def
|
114
|
-
|
115
|
-
@
|
98
|
+
def presented?
|
99
|
+
ensure_construction
|
100
|
+
@constructed
|
116
101
|
end
|
117
102
|
|
118
|
-
def
|
119
|
-
|
120
|
-
|
103
|
+
def content
|
104
|
+
return unless view
|
105
|
+
view.to_html
|
121
106
|
end
|
122
107
|
|
123
|
-
def
|
124
|
-
|
108
|
+
def view
|
109
|
+
ensure_construction
|
110
|
+
@view
|
125
111
|
end
|
126
112
|
|
127
|
-
def
|
128
|
-
@
|
113
|
+
def partial(name)
|
114
|
+
store.partial(@path, name)
|
129
115
|
end
|
130
116
|
|
131
|
-
def
|
132
|
-
@
|
133
|
-
@
|
117
|
+
def view=(view)
|
118
|
+
@view = view
|
119
|
+
@constructed = true
|
134
120
|
end
|
135
121
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
122
|
+
def template=(template)
|
123
|
+
unless template.is_a?(Template)
|
124
|
+
# get template by name
|
125
|
+
template = store.template(template)
|
126
|
+
end
|
139
127
|
|
140
|
-
|
141
|
-
@
|
142
|
-
@root = nil
|
143
|
-
@root_path = abstract_path
|
128
|
+
@template = template
|
129
|
+
@constructed = false
|
144
130
|
end
|
145
131
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
def with_container(container, &block)
|
150
|
-
v = self.view.find("##{container}").first
|
151
|
-
ViewContext.new(v).instance_exec(v, &block)
|
132
|
+
def page=(page)
|
133
|
+
@page = page
|
134
|
+
@constructed = false
|
152
135
|
end
|
153
136
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
def ensure_root_view_built
|
159
|
-
build_root_view unless @root_view_is_built
|
137
|
+
def path=(path)
|
138
|
+
@path = path
|
139
|
+
setup_for_path(true)
|
160
140
|
end
|
161
141
|
|
162
|
-
|
163
|
-
|
164
|
-
|
142
|
+
def ensure_construction
|
143
|
+
# only construct once
|
144
|
+
return if @constructed
|
165
145
|
|
166
|
-
|
167
|
-
@
|
168
|
-
@presented = false
|
169
|
-
@root_path = nil
|
170
|
-
@root_view_is_built = false
|
171
|
-
@root_view = nil
|
172
|
-
@view_path = nil
|
173
|
-
@container_name = nil
|
174
|
-
end
|
146
|
+
# if no template/page was found, we can't construct
|
147
|
+
return if @template.nil? || @page.nil?
|
175
148
|
|
176
|
-
|
177
|
-
@
|
149
|
+
# construct
|
150
|
+
@view = @template.dup.build(@page)
|
151
|
+
@constructed = true
|
152
|
+
end
|
178
153
|
|
179
|
-
|
154
|
+
protected
|
180
155
|
|
181
|
-
|
182
|
-
|
156
|
+
def setup_for_path(explicit = false)
|
157
|
+
self.template = store.template(@path)
|
158
|
+
self.page = store.page(@path)
|
159
|
+
self.view = store.view(@path)
|
183
160
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
@root_view = r_v.dup
|
189
|
-
@presented = true
|
190
|
-
else
|
191
|
-
@root_view = populate_view(LazyView.new(@root_path, true), view_info[:views])
|
192
|
-
@presented = true
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
def restful_view_path(restful_info)
|
197
|
-
if restful_info[:restful_action] == :show
|
198
|
-
"#{StringUtils.remove_route_vars(@request.route_spec)}/show"
|
199
|
-
else
|
200
|
-
StringUtils.remove_route_vars(@request.route_spec)
|
201
|
-
end
|
161
|
+
@constructed = true
|
162
|
+
rescue MissingView => e # catches no view path error
|
163
|
+
explicit ? raise(e) : Pakyow.logger.debug(e.message)
|
164
|
+
@constructed = false
|
202
165
|
end
|
203
166
|
|
204
167
|
def load_views
|
205
168
|
@view_stores = {}
|
206
|
-
Configuration::Presenter.view_stores.each_pair {|name, path|
|
207
|
-
@view_stores[name] = ViewLookupStore.new(path)
|
208
|
-
}
|
209
|
-
|
210
|
-
if Configuration::Base.presenter.view_caching then
|
211
|
-
@populated_root_view_cache = build_root_view_cache(self.current_view_lookup_store.view_info)
|
212
|
-
end
|
213
|
-
end
|
214
169
|
|
215
|
-
|
216
|
-
|
217
|
-
view_info.each{|dir,info|
|
218
|
-
r_v = LazyView.new(info[:root_view], true)
|
219
|
-
populate_view(r_v, info[:views])
|
220
|
-
key = [dir, info[:root_view]]
|
221
|
-
cache.put(key, r_v)
|
170
|
+
Config::Presenter.view_stores.each_pair {|name, path|
|
171
|
+
@view_stores[name] = ViewStore.new(path, name)
|
222
172
|
}
|
223
|
-
cache
|
224
173
|
end
|
225
174
|
|
226
|
-
|
227
|
-
|
228
|
-
def populate_view(top_view, views)
|
229
|
-
top_view.containers.each {|e|
|
230
|
-
next unless path = views[e[:name]]
|
175
|
+
def load_bindings
|
176
|
+
@binder = Binder.instance.reset
|
231
177
|
|
232
|
-
|
233
|
-
|
234
|
-
self.add_content_to_container(v, e[:doc])
|
178
|
+
Pakyow::App.bindings.each_pair {|set_name, block|
|
179
|
+
@binder.set(set_name, &block)
|
235
180
|
}
|
236
|
-
top_view
|
237
|
-
end
|
238
|
-
|
239
|
-
def parser(format, &block)
|
240
|
-
@parser_store ||= {}
|
241
|
-
@parser_store[format.to_sym] = block
|
242
181
|
end
|
243
182
|
|
244
|
-
def
|
245
|
-
|
246
|
-
container.add_child(content)
|
183
|
+
def load_processors
|
184
|
+
@processor_store = Pakyow::App.processors
|
247
185
|
end
|
248
|
-
|
249
|
-
def reset_container(container)
|
250
|
-
container.inner_html = ''
|
251
|
-
end
|
252
|
-
|
253
186
|
end
|
254
187
|
end
|
255
188
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Pakyow
|
2
|
+
module Presenter
|
3
|
+
class Template
|
4
|
+
include DocHelpers
|
5
|
+
attr_accessor :name, :doc
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def load(path)
|
9
|
+
format = StringUtils.split_at_last_dot(path)[-1]
|
10
|
+
contents = File.read(path)
|
11
|
+
name = File.basename(path, '.*').to_sym
|
12
|
+
|
13
|
+
return self.new(name, contents, format)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(name, contents, format = :html)
|
18
|
+
@name = name
|
19
|
+
|
20
|
+
if contents.is_a?(Nokogiri::HTML::Document)
|
21
|
+
@doc = contents
|
22
|
+
else
|
23
|
+
processed = Presenter.process(contents, format)
|
24
|
+
@doc = Nokogiri::HTML::Document.parse(processed)
|
25
|
+
end
|
26
|
+
|
27
|
+
containers
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize_copy(original_template)
|
31
|
+
super
|
32
|
+
|
33
|
+
# copy doc
|
34
|
+
@doc = original_template.doc.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
def container(name = :default)
|
38
|
+
container = @containers[name.to_sym]
|
39
|
+
return view_from_path(container[:path])
|
40
|
+
end
|
41
|
+
|
42
|
+
def containers(refind = false)
|
43
|
+
@containers = (!@containers || refind) ? find_containers : @containers
|
44
|
+
end
|
45
|
+
|
46
|
+
def build(page)
|
47
|
+
# add content to each container
|
48
|
+
containers.each do |container|
|
49
|
+
name = container[0]
|
50
|
+
|
51
|
+
if content = page.content(name)
|
52
|
+
container(name).replace(content)
|
53
|
+
else
|
54
|
+
Pakyow.logger.debug "No content for '#{name}' in page '#{page.path}'"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
return View.from_doc(doc)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# returns an array of hashes, each with the container name and doc
|
64
|
+
def find_containers
|
65
|
+
containers = {}
|
66
|
+
|
67
|
+
@doc.traverse {|e|
|
68
|
+
next unless e.is_a?(Nokogiri::XML::Comment)
|
69
|
+
next unless match = e.text.strip.match(/@container( ([a-zA-Z0-9\-_]*))*/)
|
70
|
+
name = match[2] || :default
|
71
|
+
|
72
|
+
containers[name.to_sym] = { doc: e, path: path_to(e) }
|
73
|
+
}
|
74
|
+
|
75
|
+
return containers
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|