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.
- checksums.yaml +5 -5
- data/{pakyow-presenter/CHANGELOG.md → CHANGELOG.md} +0 -5
- data/LICENSE +4 -0
- data/{pakyow-presenter/README.md → README.md} +1 -2
- data/lib/pakyow/generators/presenter/presenter_generator.rb +28 -0
- data/lib/pakyow/generators/presenter/templates/presenter.rb.tt +2 -0
- data/lib/pakyow/plugin/helpers/rendering.rb +19 -0
- data/lib/pakyow/presenter/actions/auto_render.rb +15 -0
- data/lib/pakyow/presenter/attributes/attribute.rb +22 -0
- data/lib/pakyow/presenter/attributes/boolean.rb +24 -0
- data/lib/pakyow/presenter/attributes/hash.rb +89 -0
- data/lib/pakyow/presenter/attributes/set.rb +61 -0
- data/lib/pakyow/presenter/attributes/string.rb +38 -0
- data/lib/pakyow/presenter/attributes.rb +126 -0
- data/lib/pakyow/presenter/behavior/config.rb +29 -0
- data/lib/pakyow/presenter/behavior/error_rendering.rb +124 -0
- data/lib/pakyow/presenter/behavior/exposures.rb +29 -0
- data/lib/pakyow/presenter/behavior/implicit_rendering.rb +21 -0
- data/lib/pakyow/presenter/behavior/initializing.rb +37 -0
- data/lib/pakyow/presenter/behavior/modes.rb +117 -0
- data/lib/pakyow/presenter/behavior/versions.rb +71 -0
- data/lib/pakyow/presenter/behavior/watching.rb +21 -0
- data/lib/pakyow/presenter/binder.rb +115 -0
- data/lib/pakyow/presenter/binding_parts.rb +64 -0
- data/lib/pakyow/presenter/component.rb +74 -0
- data/lib/pakyow/presenter/composers/component.rb +78 -0
- data/lib/pakyow/presenter/composers/view.rb +73 -0
- data/lib/pakyow/presenter/errors.rb +63 -0
- data/lib/pakyow/presenter/framework.rb +203 -0
- data/lib/pakyow/presenter/front_matter_parser.rb +42 -0
- data/lib/pakyow/presenter/helpers/rendering.rb +18 -0
- data/lib/pakyow/presenter/presentable.rb +28 -0
- data/lib/pakyow/presenter/presentable_error.rb +19 -0
- data/lib/pakyow/presenter/presenter.rb +766 -0
- data/lib/pakyow/presenter/presenters/endpoint.rb +158 -0
- data/lib/pakyow/presenter/presenters/form.rb +522 -0
- data/lib/pakyow/presenter/processor.rb +61 -0
- data/lib/pakyow/presenter/renderable.rb +28 -0
- data/lib/pakyow/presenter/renderer.rb +269 -0
- data/lib/pakyow/presenter/rendering/actions/cleanup_prototype_nodes.rb +21 -0
- data/lib/pakyow/presenter/rendering/actions/cleanup_unbound_bindings.rb +35 -0
- data/lib/pakyow/presenter/rendering/actions/create_template_nodes.rb +27 -0
- data/lib/pakyow/presenter/rendering/actions/insert_prototype_bar.rb +101 -0
- data/lib/pakyow/presenter/rendering/actions/install_authenticity.rb +42 -0
- data/lib/pakyow/presenter/rendering/actions/place_in_mode.rb +56 -0
- data/lib/pakyow/presenter/rendering/actions/render_components.rb +279 -0
- data/lib/pakyow/presenter/rendering/actions/set_page_title.rb +35 -0
- data/lib/pakyow/presenter/rendering/actions/setup_endpoints.rb +62 -0
- data/lib/pakyow/presenter/rendering/actions/setup_forms.rb +174 -0
- data/lib/pakyow/presenter/significant_nodes.rb +309 -0
- data/lib/pakyow/presenter/templates.rb +229 -0
- data/lib/pakyow/presenter/versioned_view.rb +209 -0
- data/lib/pakyow/presenter/view.rb +586 -0
- data/lib/pakyow/presenter/views/form.rb +58 -0
- data/lib/pakyow/presenter/views/layout.rb +32 -0
- data/lib/pakyow/presenter/views/page.rb +72 -0
- data/lib/pakyow/presenter/views/partial.rb +28 -0
- data/lib/pakyow/presenter.rb +33 -0
- data/lib/pakyow/views/errors/layouts/development_error.html +102 -0
- data/lib/pakyow/views/errors/layouts/production_error.html +27 -0
- data/lib/pakyow/views/errors/pages/404.html +12 -0
- data/lib/pakyow/views/errors/pages/500.html +12 -0
- data/lib/pakyow/views/errors/pages/development/500.html +39 -0
- data/lib/string_doc/attributes.rb +109 -0
- data/lib/string_doc/meta_attributes.rb +48 -0
- data/lib/string_doc/meta_node.rb +328 -0
- data/lib/string_doc/node.rb +377 -0
- data/lib/string_doc.rb +642 -0
- metadata +95 -75
- data/pakyow-presenter/LICENSE +0 -20
- data/pakyow-presenter/lib/pakyow/presenter/attributes.rb +0 -228
- data/pakyow-presenter/lib/pakyow/presenter/base.rb +0 -38
- data/pakyow-presenter/lib/pakyow/presenter/binder.rb +0 -116
- data/pakyow-presenter/lib/pakyow/presenter/binder_set.rb +0 -94
- data/pakyow-presenter/lib/pakyow/presenter/binding_eval.rb +0 -37
- data/pakyow-presenter/lib/pakyow/presenter/config/presenter.rb +0 -42
- data/pakyow-presenter/lib/pakyow/presenter/container.rb +0 -6
- data/pakyow-presenter/lib/pakyow/presenter/doc_helpers.rb +0 -17
- data/pakyow-presenter/lib/pakyow/presenter/exceptions.rb +0 -11
- data/pakyow-presenter/lib/pakyow/presenter/ext/app.rb +0 -33
- data/pakyow-presenter/lib/pakyow/presenter/ext/call_context.rb +0 -28
- data/pakyow-presenter/lib/pakyow/presenter/helpers.rb +0 -46
- data/pakyow-presenter/lib/pakyow/presenter/page.rb +0 -110
- data/pakyow-presenter/lib/pakyow/presenter/partial.rb +0 -6
- data/pakyow-presenter/lib/pakyow/presenter/presenter.rb +0 -232
- data/pakyow-presenter/lib/pakyow/presenter/string_doc.rb +0 -380
- data/pakyow-presenter/lib/pakyow/presenter/string_doc_parser.rb +0 -144
- data/pakyow-presenter/lib/pakyow/presenter/string_doc_renderer.rb +0 -18
- data/pakyow-presenter/lib/pakyow/presenter/template.rb +0 -51
- data/pakyow-presenter/lib/pakyow/presenter/view.rb +0 -541
- data/pakyow-presenter/lib/pakyow/presenter/view_collection.rb +0 -330
- data/pakyow-presenter/lib/pakyow/presenter/view_composer.rb +0 -237
- data/pakyow-presenter/lib/pakyow/presenter/view_context.rb +0 -111
- data/pakyow-presenter/lib/pakyow/presenter/view_store.rb +0 -262
- data/pakyow-presenter/lib/pakyow/presenter/view_store_loader.rb +0 -43
- data/pakyow-presenter/lib/pakyow/presenter/view_version.rb +0 -113
- data/pakyow-presenter/lib/pakyow/presenter.rb +0 -8
- data/pakyow-presenter/lib/pakyow/views/errors/404.erb +0 -26
- data/pakyow-presenter/lib/pakyow/views/errors/500.erb +0 -23
- data/pakyow-presenter/lib/pakyow-presenter.rb +0 -1
@@ -1,94 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module Presenter
|
3
|
-
class BinderSet
|
4
|
-
attr_reader :scopes
|
5
|
-
|
6
|
-
def initialize(&block)
|
7
|
-
@scopes = {}
|
8
|
-
@options = {}
|
9
|
-
@config = {}
|
10
|
-
|
11
|
-
instance_exec(&block)
|
12
|
-
end
|
13
|
-
|
14
|
-
def scope(name, &block)
|
15
|
-
scope_eval = ScopeEval.new
|
16
|
-
bindings, options, config = scope_eval.eval(&block)
|
17
|
-
|
18
|
-
@scopes[name.to_sym] = bindings
|
19
|
-
@options[name.to_sym] = options
|
20
|
-
@config[name.to_sym] = config
|
21
|
-
end
|
22
|
-
|
23
|
-
def match_for_prop(prop, scope, bindable, bindings = {})
|
24
|
-
return bindings_for_scope(scope, bindings)[prop]
|
25
|
-
end
|
26
|
-
|
27
|
-
def options_for_prop(scope, prop, bindable, context)
|
28
|
-
if block = (@options[scope] || {})[prop]
|
29
|
-
binding_eval = BindingEval.new(prop, bindable, context)
|
30
|
-
values = binding_eval.instance_exec(binding_eval.value, bindable, context, &block)
|
31
|
-
values.unshift(['', '']) if @config[scope][prop][:empty]
|
32
|
-
values
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def has_prop?(scope, prop, bindings)
|
37
|
-
bindings_for_scope(scope, bindings).key? prop
|
38
|
-
end
|
39
|
-
|
40
|
-
def bindings_for_scope(scope, bindings)
|
41
|
-
# merge passed bindings with bindings
|
42
|
-
(@scopes[scope] || {}).merge(bindings)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class ScopeEval
|
47
|
-
include Helpers
|
48
|
-
|
49
|
-
def initialize
|
50
|
-
@bindings = {}
|
51
|
-
@options = {}
|
52
|
-
@config = {}
|
53
|
-
end
|
54
|
-
|
55
|
-
def eval(&block)
|
56
|
-
self.instance_eval(&block)
|
57
|
-
return @bindings, @options, @config
|
58
|
-
end
|
59
|
-
|
60
|
-
def binding(name, &block)
|
61
|
-
@bindings[name.to_sym] = block
|
62
|
-
end
|
63
|
-
|
64
|
-
def options(name, empty: false, &block)
|
65
|
-
@options[name] = block
|
66
|
-
@config[name] = { empty: empty }
|
67
|
-
end
|
68
|
-
|
69
|
-
def restful(route_group)
|
70
|
-
binding :_root do
|
71
|
-
part :view do |view|
|
72
|
-
routes = Router.instance.group(route_group)
|
73
|
-
action = view.attrs.action.value
|
74
|
-
next if (action && !action.empty?)
|
75
|
-
|
76
|
-
route_params = params.dup
|
77
|
-
if view.doc.tagname == 'form'
|
78
|
-
if id = bindable[:id]
|
79
|
-
view.prepend(View.new('<input type="hidden" name="_method" value="patch">'))
|
80
|
-
route_params[:"#{route_group}_id"] = id
|
81
|
-
action = :update
|
82
|
-
else
|
83
|
-
action = :create
|
84
|
-
end
|
85
|
-
|
86
|
-
view.attrs.action = routes.path(action, route_params)
|
87
|
-
view.attrs.method = 'post'
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module Presenter
|
3
|
-
class BindingEval
|
4
|
-
include Pakyow::Helpers
|
5
|
-
|
6
|
-
attr_reader :context, :bindable
|
7
|
-
|
8
|
-
def initialize(prop, bindable, context)
|
9
|
-
@parts = {}
|
10
|
-
@prop = prop
|
11
|
-
@bindable = bindable
|
12
|
-
@context = context
|
13
|
-
end
|
14
|
-
|
15
|
-
def value
|
16
|
-
if bindable.is_a?(Hash)
|
17
|
-
bindable[@prop]
|
18
|
-
elsif bindable.respond_to?(@prop)
|
19
|
-
bindable.send(@prop)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def eval(&block)
|
24
|
-
ret = instance_exec(value, bindable, context, &block)
|
25
|
-
if ret.respond_to?(:to_hash)
|
26
|
-
@parts.merge!(ret.to_hash)
|
27
|
-
end
|
28
|
-
@parts.empty? ? ret : @parts
|
29
|
-
end
|
30
|
-
|
31
|
-
def part(name, &block)
|
32
|
-
@parts[name.to_sym] = block
|
33
|
-
nil # Return nil so #part return value is ignored
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
Pakyow::Config.register :presenter do |config|
|
2
|
-
# registered view stores
|
3
|
-
config.opt :view_stores, -> {
|
4
|
-
@stores ||= {
|
5
|
-
default: File.join(Pakyow::Config.app.root, 'app', 'views')
|
6
|
-
}
|
7
|
-
}
|
8
|
-
|
9
|
-
# the default view for each view store
|
10
|
-
config.opt :default_views, { default: :default }
|
11
|
-
|
12
|
-
# a convenience option to lookup the default_view for a view store by name
|
13
|
-
config.opt :default_view, -> (store_name) {
|
14
|
-
views = Pakyow::Config.presenter.default_views
|
15
|
-
views.fetch(store_name) { views[:default] }
|
16
|
-
}
|
17
|
-
|
18
|
-
# the default template dir for each view store
|
19
|
-
config.opt :template_dirs, { default: '_templates' }
|
20
|
-
|
21
|
-
# a convenience option to lookup the template_dir for a view store by name
|
22
|
-
config.opt :template_dir, -> (store_name) {
|
23
|
-
dirs = Pakyow::Config.presenter.template_dirs
|
24
|
-
dirs.fetch(store_name) { dirs[:default] }
|
25
|
-
}
|
26
|
-
|
27
|
-
# the attribute expected for scope definitions
|
28
|
-
config.opt :scope_attribute, 'data-scope'
|
29
|
-
|
30
|
-
# the attribute expected for prop definitions
|
31
|
-
config.opt :prop_attribute, 'data-prop'
|
32
|
-
|
33
|
-
# if true, views are visible without a route defined
|
34
|
-
config.opt :require_route, true
|
35
|
-
|
36
|
-
# the document class used to parse and render views
|
37
|
-
config.opt :view_doc_class, Pakyow::Presenter::StringDoc
|
38
|
-
end.env :development do |opts|
|
39
|
-
opts.require_route = false
|
40
|
-
end.env :production do |opts|
|
41
|
-
opts.require_route = true
|
42
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module Presenter
|
3
|
-
module DocHelpers
|
4
|
-
def self.self_closing_tag?(tag)
|
5
|
-
%w[area base basefont br hr input img link meta].include? tag
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.form_field?(tag)
|
9
|
-
%w[input select textarea button].include? tag
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.tag_without_value?(tag)
|
13
|
-
%w[select].include? tag
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module Presenter
|
3
|
-
class MissingTemplatesDir < Error; end
|
4
|
-
class MissingView < Error; end
|
5
|
-
class MissingTemplate < Error; end
|
6
|
-
class MissingPage < Error; end
|
7
|
-
class MissingPartial < Error; end
|
8
|
-
class MissingComposer < Error; end
|
9
|
-
class MissingContainer < Error; end
|
10
|
-
end
|
11
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
class App
|
3
|
-
class << self
|
4
|
-
RESOURCE_ACTIONS[:presenter] = Proc.new { |app, set_name, _, _|
|
5
|
-
app.bindings(set_name) { scope(set_name) { restful(set_name) } }
|
6
|
-
}
|
7
|
-
|
8
|
-
def bindings(set_name = :main, &block)
|
9
|
-
if set_name && block
|
10
|
-
bindings[set_name] = block
|
11
|
-
else
|
12
|
-
@bindings ||= {}
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def processor(*args, &block)
|
17
|
-
args.each {|format|
|
18
|
-
processors[format] = block
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
|
-
def processors
|
23
|
-
@processors ||= {}
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Convenience method for defining bindings on an app instance.
|
28
|
-
#
|
29
|
-
def bindings(set_name = :main, &block)
|
30
|
-
self.class.bindings(set_name, &block)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'erb'
|
2
|
-
|
3
|
-
module Pakyow
|
4
|
-
class CallContext
|
5
|
-
protected
|
6
|
-
|
7
|
-
def presenter_handle_error(code)
|
8
|
-
return if !config.app.errors_in_browser || req.format != :html
|
9
|
-
response.body = [content_for_code(code)]
|
10
|
-
end
|
11
|
-
|
12
|
-
def content_for_code(code)
|
13
|
-
content = ERB.new(File.read(path_for_code(code))).result(binding)
|
14
|
-
page = Presenter::Page.new(:presenter, content, '/')
|
15
|
-
composer = presenter.compose_at('/', page: page)
|
16
|
-
composer.to_html
|
17
|
-
end
|
18
|
-
|
19
|
-
def path_for_code(code)
|
20
|
-
File.join(
|
21
|
-
File.expand_path('../../../', __FILE__),
|
22
|
-
'views',
|
23
|
-
'errors',
|
24
|
-
code.to_s + '.erb'
|
25
|
-
)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
module Pakyow
|
4
|
-
module Helpers
|
5
|
-
module Context
|
6
|
-
extend Forwardable
|
7
|
-
|
8
|
-
def_delegators :@presenter, :store, :store=, :content, :view=,
|
9
|
-
:template=, :page=, :path, :path=, :compose, :composer, :precompose!
|
10
|
-
|
11
|
-
def view
|
12
|
-
Presenter::ViewContext.new(presenter.view, self)
|
13
|
-
end
|
14
|
-
|
15
|
-
def partial(*args)
|
16
|
-
Presenter::ViewContext.new(presenter.partial(*args), self)
|
17
|
-
end
|
18
|
-
|
19
|
-
def template
|
20
|
-
Presenter::ViewContext.new(presenter.template, self)
|
21
|
-
end
|
22
|
-
|
23
|
-
def page
|
24
|
-
Presenter::ViewContext.new(presenter.page, self)
|
25
|
-
end
|
26
|
-
|
27
|
-
def container(*args)
|
28
|
-
Presenter::ViewContext.new(presenter.container(*args), self)
|
29
|
-
end
|
30
|
-
|
31
|
-
def bindings(name)
|
32
|
-
presenter.bindings(name).bindings
|
33
|
-
end
|
34
|
-
|
35
|
-
def presenter
|
36
|
-
@presenter
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
module App
|
41
|
-
def presenter
|
42
|
-
@presenter
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,110 +0,0 @@
|
|
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, view_store_name = :default)
|
8
|
-
format = String.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, view_store_name)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
attr_reader :path, :contents
|
17
|
-
|
18
|
-
def initialize(name, contents, path, format = :html, view_store_name = :default)
|
19
|
-
@name, @contents, @path, @format = name, contents, path, format
|
20
|
-
|
21
|
-
@info = { template: Pakyow::App.config.presenter.default_view(view_store_name) }
|
22
|
-
@containers = {}
|
23
|
-
|
24
|
-
unless @contents.nil?
|
25
|
-
parse_info
|
26
|
-
parse_content
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize_copy(original_page)
|
31
|
-
super
|
32
|
-
|
33
|
-
# copy content
|
34
|
-
@containers = {}
|
35
|
-
original_page.instance_variable_get(:'@containers').each_pair do |k, v|
|
36
|
-
@containers[k] = v.dup
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def content(container = nil)
|
41
|
-
return @contents if container.nil?
|
42
|
-
|
43
|
-
container = @containers.fetch(container.to_sym) {
|
44
|
-
raise MissingContainer, "No container named #{container} in #{@path}"
|
45
|
-
}
|
46
|
-
|
47
|
-
return container.doc
|
48
|
-
end
|
49
|
-
|
50
|
-
def info(key = nil)
|
51
|
-
return @info if key.nil?
|
52
|
-
return @info[key]
|
53
|
-
end
|
54
|
-
|
55
|
-
def ==(page)
|
56
|
-
@contents == page.contents
|
57
|
-
end
|
58
|
-
|
59
|
-
def container(name)
|
60
|
-
@containers.fetch(name.to_sym) {
|
61
|
-
raise MissingContainer, "No container named #{name} in #{@path}"
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
def each_container
|
66
|
-
@containers.each_pair { |name, container| yield(name, container) }
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
def parse_info
|
72
|
-
info = parse_front_matter(@contents)
|
73
|
-
info = {} if !info || !info.is_a?(Hash)
|
74
|
-
|
75
|
-
@info.merge!(Hash.symbolize(info))
|
76
|
-
end
|
77
|
-
|
78
|
-
def parse_content
|
79
|
-
# remove yaml front matter
|
80
|
-
@contents.gsub!(/---(.|\n)*---/, '')
|
81
|
-
|
82
|
-
# process contents
|
83
|
-
@contents = Presenter.process(@contents, @format)
|
84
|
-
|
85
|
-
# find content in named containers
|
86
|
-
within_regex = /<!--\s*@within\s*([a-zA-Z0-9\-_]*)\s*-->(.*?)<!--\s*\/within\s*-->/m
|
87
|
-
|
88
|
-
@contents.scan(within_regex) do |m|
|
89
|
-
container_name = m[0].to_sym
|
90
|
-
@containers[container_name] = Container.new(m[1])
|
91
|
-
end
|
92
|
-
|
93
|
-
# find default content
|
94
|
-
@containers[:default] = Container.new(@contents.gsub(within_regex, ''))
|
95
|
-
end
|
96
|
-
|
97
|
-
def parse_front_matter(contents)
|
98
|
-
# match the matter
|
99
|
-
matter = contents.match(MATTER_MATCHER).to_s
|
100
|
-
|
101
|
-
# remove the opening/closing '---'
|
102
|
-
matter = matter.split("\n")[1..-2]
|
103
|
-
# return if no matter
|
104
|
-
return {} if matter.nil?
|
105
|
-
|
106
|
-
YAML.load(matter.join("\n"))
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
@@ -1,232 +0,0 @@
|
|
1
|
-
module Pakyow
|
2
|
-
module Presenter
|
3
|
-
class Presenter
|
4
|
-
class << self
|
5
|
-
def process(contents, format)
|
6
|
-
format = format.to_sym
|
7
|
-
|
8
|
-
unless app = Pakyow.app
|
9
|
-
return contents
|
10
|
-
end
|
11
|
-
|
12
|
-
unless presenter = app.presenter
|
13
|
-
return contents
|
14
|
-
end
|
15
|
-
|
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
|
20
|
-
|
21
|
-
processed = processor.call(contents)
|
22
|
-
|
23
|
-
# reprocess html content unless we just did that
|
24
|
-
return processed if format == :html
|
25
|
-
process(processed, :html)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
Pakyow::App.before(:init) {
|
30
|
-
@presenter = Presenter.new
|
31
|
-
ViewStoreLoader.instance.reset
|
32
|
-
}
|
33
|
-
|
34
|
-
Pakyow::App.after(:match) {
|
35
|
-
@presenter = Pakyow.app.presenter.dup
|
36
|
-
@presenter.prepare_with_context(context)
|
37
|
-
}
|
38
|
-
|
39
|
-
Pakyow::App.after(:route) {
|
40
|
-
if Config.presenter.require_route && !found?
|
41
|
-
@found
|
42
|
-
else
|
43
|
-
if @presenter.presented?
|
44
|
-
@found = true
|
45
|
-
@context.response.body = [@presenter.content]
|
46
|
-
else
|
47
|
-
@found = false unless found?
|
48
|
-
end
|
49
|
-
end
|
50
|
-
}
|
51
|
-
|
52
|
-
Pakyow::App.after(:load) {
|
53
|
-
@presenter.load
|
54
|
-
}
|
55
|
-
|
56
|
-
Pakyow::App.after(:error) {
|
57
|
-
unless config.app.errors_in_browser
|
58
|
-
@context.response.body = [@presenter.content] if @presenter.presented?
|
59
|
-
end
|
60
|
-
}
|
61
|
-
|
62
|
-
attr_accessor :processor_store, :binder, :path, :context, :composer
|
63
|
-
|
64
|
-
def initialize
|
65
|
-
setup
|
66
|
-
end
|
67
|
-
|
68
|
-
def store(name = nil)
|
69
|
-
if name
|
70
|
-
@view_stores[name]
|
71
|
-
else
|
72
|
-
@view_stores[@store]
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def store=(name)
|
77
|
-
@store = name
|
78
|
-
|
79
|
-
return unless has_path?
|
80
|
-
setup_for_path(@path)
|
81
|
-
end
|
82
|
-
|
83
|
-
def load
|
84
|
-
load_processors
|
85
|
-
load_views
|
86
|
-
load_bindings
|
87
|
-
end
|
88
|
-
|
89
|
-
def prepare_with_context(context)
|
90
|
-
@context = context
|
91
|
-
|
92
|
-
if @context.request.has_route_vars?
|
93
|
-
@path = String.remove_route_vars(@context.request.route_path)
|
94
|
-
else
|
95
|
-
@path = @context.request.path
|
96
|
-
end
|
97
|
-
|
98
|
-
setup
|
99
|
-
end
|
100
|
-
|
101
|
-
def presented?
|
102
|
-
!view.nil?
|
103
|
-
rescue MissingView
|
104
|
-
false
|
105
|
-
end
|
106
|
-
|
107
|
-
def content
|
108
|
-
to_present = view
|
109
|
-
to_present.is_a?(ViewComposer) ? to_present.composed.to_html : to_present.to_html
|
110
|
-
end
|
111
|
-
|
112
|
-
def view
|
113
|
-
@composer || @view || raise(MissingView)
|
114
|
-
end
|
115
|
-
|
116
|
-
def view?(path)
|
117
|
-
if composer_for_path(path)
|
118
|
-
true
|
119
|
-
else
|
120
|
-
false
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def precompose!
|
125
|
-
self.view = @composer.view
|
126
|
-
end
|
127
|
-
|
128
|
-
def view=(view)
|
129
|
-
@view = view
|
130
|
-
|
131
|
-
# setting a view means we no longer use/need the composer
|
132
|
-
@composer = nil
|
133
|
-
end
|
134
|
-
|
135
|
-
def template=(template)
|
136
|
-
raise MissingComposer 'Cannot set template without a composer' if @composer.nil?
|
137
|
-
@composer.template = template
|
138
|
-
end
|
139
|
-
|
140
|
-
def template
|
141
|
-
raise MissingComposer 'Cannot get template without a composer' if @composer.nil?
|
142
|
-
@composer.template
|
143
|
-
end
|
144
|
-
|
145
|
-
def page=(page)
|
146
|
-
raise MissingComposer, 'Cannot set page without a composer' if @composer.nil?
|
147
|
-
@composer.page = page
|
148
|
-
end
|
149
|
-
|
150
|
-
def page
|
151
|
-
raise MissingComposer 'Cannot get page without a composer' if @composer.nil?
|
152
|
-
@composer.page
|
153
|
-
end
|
154
|
-
|
155
|
-
def container(name)
|
156
|
-
raise MissingComposer 'Cannot get container without a composer' if @composer.nil?
|
157
|
-
@composer.container(name)
|
158
|
-
end
|
159
|
-
|
160
|
-
def partial(name)
|
161
|
-
raise MissingComposer 'Cannot get partial without a composer' if @composer.nil?
|
162
|
-
@composer.partial(name)
|
163
|
-
end
|
164
|
-
|
165
|
-
def path=(path)
|
166
|
-
setup_for_path(path, true)
|
167
|
-
end
|
168
|
-
|
169
|
-
def compose(opts = {}, &block)
|
170
|
-
compose_at(@path, opts, &block)
|
171
|
-
end
|
172
|
-
|
173
|
-
def compose_at(path, opts = {}, &block)
|
174
|
-
@composer = ViewComposer.from_path(store, path, opts, &block)
|
175
|
-
end
|
176
|
-
|
177
|
-
def has_path?
|
178
|
-
!@path.nil?
|
179
|
-
end
|
180
|
-
|
181
|
-
protected
|
182
|
-
|
183
|
-
def setup
|
184
|
-
@view, @composer = nil
|
185
|
-
self.store = :default
|
186
|
-
end
|
187
|
-
|
188
|
-
def setup_for_path(path, explicit = false)
|
189
|
-
if composer = composer_for_path(path)
|
190
|
-
@composer = composer
|
191
|
-
@path = path
|
192
|
-
return
|
193
|
-
end
|
194
|
-
|
195
|
-
e = MissingView.new("No view at path '#{path}'")
|
196
|
-
explicit ? raise(e) : Pakyow.logger.info(e.message)
|
197
|
-
end
|
198
|
-
|
199
|
-
def load_views
|
200
|
-
@view_stores ||= {}
|
201
|
-
|
202
|
-
Pakyow::Config.presenter.view_stores.each_pair { |name, path|
|
203
|
-
next unless ViewStoreLoader.instance.modified?(name, path)
|
204
|
-
@view_stores[name] = ViewStore.new(path, name)
|
205
|
-
}
|
206
|
-
end
|
207
|
-
|
208
|
-
def load_bindings
|
209
|
-
@binder = Binder.instance.reset
|
210
|
-
|
211
|
-
Pakyow::App.bindings.each_pair {|set_name, block|
|
212
|
-
@binder.set(set_name, &block)
|
213
|
-
}
|
214
|
-
end
|
215
|
-
|
216
|
-
def load_processors
|
217
|
-
@processor_store = Pakyow::App.processors
|
218
|
-
end
|
219
|
-
|
220
|
-
def composer_for_path(path)
|
221
|
-
@view_stores.each do |name, store|
|
222
|
-
begin
|
223
|
-
return store.composer(path)
|
224
|
-
rescue MissingView
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
return nil
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|