express_templates 0.5.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -41
- data/lib/arbre/patches.rb +50 -0
- data/lib/core_extensions/proc.rb +1 -0
- data/lib/express_templates/compiler.rb +2 -7
- data/lib/express_templates/components/base.rb +33 -37
- data/lib/express_templates/components/capabilities/resourceful.rb +30 -18
- data/lib/express_templates/components/configurable.rb +41 -0
- data/lib/express_templates/components/form_rails_support.rb +5 -6
- data/lib/express_templates/components/forms/basic_fields.rb +3 -6
- data/lib/express_templates/components/forms/checkbox.rb +1 -1
- data/lib/express_templates/components/forms/express_form.rb +15 -25
- data/lib/express_templates/components/forms/form_component.rb +20 -11
- data/lib/express_templates/components/forms/option_support.rb +2 -2
- data/lib/express_templates/components/forms/radio.rb +7 -8
- data/lib/express_templates/components/forms/select.rb +61 -37
- data/lib/express_templates/components/forms/select_collection.rb +2 -8
- data/lib/express_templates/components/forms/submit.rb +7 -6
- data/lib/express_templates/components/forms.rb +0 -1
- data/lib/express_templates/components/tree_for.rb +26 -42
- data/lib/express_templates/components.rb +1 -9
- data/lib/express_templates/version.rb +1 -1
- data/lib/express_templates.rb +2 -3
- data/test/components/base_test.rb +17 -15
- data/test/components/capabilities/resourceful_test.rb +3 -3
- data/test/components/configurable_test.rb +27 -21
- data/test/components/forms/basic_fields_test.rb +57 -17
- data/test/components/forms/checkbox_test.rb +16 -22
- data/test/components/forms/express_form_test.rb +23 -76
- data/test/components/forms/radio_test.rb +31 -27
- data/test/components/forms/select_test.rb +46 -71
- data/test/components/forms/submit_test.rb +10 -5
- data/test/components/tree_for_test.rb +24 -52
- data/test/dummy/app/views/hello/show.html.et +4 -3
- data/test/dummy/app/views/layouts/application.html.et +1 -1
- data/test/dummy/log/test.log +4794 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/-cDLO4NJHMndDHchOLz8KLD7QBc68WtaAbYxK2r6GsU.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/0t68EIwZ96sAqQSxFka9MQyIk4viw8ZIoREMCGiJRx0.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/2zt1IbQCnmzGyeZS_I4sYQzrgneSCKIJBRxcffVHlWY.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/EDVlXrcn_wEfaZ5nc_4QJBT7lPiIBcX96jBo7PBz-vc.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Eso3tkb79hXQ1XdQgjbV03KyQwSeZFAHxVHImsjQ-HQ.cache +2 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/I8LhheC1OlOyvp_qY8kWpqKcZFiqv_BO-l5bExvDEi0.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Kf4VrP5sW-qzziYSN-m7p4nETjLiEwwwRfwUOm7rOr0.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/QJ0Z8hROMfWpY6f5Chb7GSuDHE4gpabKVQTr9BCZe9s.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/T1pSAx767vY7X1UDN0vdF7YGEkI5wMSVUKopsdvp7kI.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/VlSPFPDK05c7ImadRiRVKzsaa6e15RwX77QR3NYabME.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Y7_BbgB9jgJIWYjDCsASySd26DTfPADAu_3DDnLlFog.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/g2xQygPqA5kMmp5L3wVGWaUIhdVsxFmOel9FF6M0Fuw.cache +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/kaif59owjUr3HBharCH5GUxU6KoW_zXD9a9JiTjIYPY.cache +1 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/p1cwnBd8RQBm9x5HzDmiiU7RhvnxhPX8VTfi0tTElJY.cache +3 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/qT6_0ti_v6fAjS3wETXqvTkr6W0LP4PB942uLYQzWK8.cache +3 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/y8T65xpx0s3RgT_YiqWEEXIaBsgK4woCNSuZ1zjQZBk.cache +2 -0
- data/test/express_templates_test.rb +5 -1
- data/test/handler_test.rb +19 -17
- data/test/performance_test.rb +11 -7
- data/test/test_helper.rb +162 -1
- metadata +50 -53
- data/lib/express_templates/components/capabilities/adoptable.rb +0 -20
- data/lib/express_templates/components/capabilities/building.rb +0 -14
- data/lib/express_templates/components/capabilities/conditionality.rb +0 -54
- data/lib/express_templates/components/capabilities/configurable.rb +0 -90
- data/lib/express_templates/components/capabilities/iterating.rb +0 -75
- data/lib/express_templates/components/capabilities/parenting.rb +0 -72
- data/lib/express_templates/components/capabilities/rendering.rb +0 -30
- data/lib/express_templates/components/capabilities/templating.rb +0 -198
- data/lib/express_templates/components/capabilities/wrapping.rb +0 -84
- data/lib/express_templates/components/column.rb +0 -13
- data/lib/express_templates/components/container.rb +0 -7
- data/lib/express_templates/components/content_for.rb +0 -42
- data/lib/express_templates/components/for_each.rb +0 -30
- data/lib/express_templates/components/forms/form_support.rb +0 -13
- data/lib/express_templates/components/null_wrap.rb +0 -41
- data/lib/express_templates/components/row.rb +0 -28
- data/lib/express_templates/components/table_for.rb +0 -163
- data/lib/express_templates/components/unless_block.rb +0 -40
- data/lib/express_templates/expander.rb +0 -140
- data/lib/express_templates/macro.rb +0 -45
- data/lib/express_templates/markup/html_tag.rb +0 -62
- data/lib/express_templates/markup/tag.rb +0 -150
- data/lib/express_templates/markup/wrapper.rb +0 -94
- data/lib/express_templates/markup/yielder.rb +0 -21
- data/lib/express_templates/markup.rb +0 -9
- data/test/components/column_test.rb +0 -11
- data/test/components/conditionality_test.rb +0 -37
- data/test/components/container_test.rb +0 -66
- data/test/components/content_for_test.rb +0 -60
- data/test/components/iterating_test.rb +0 -127
- data/test/components/null_wrap_test.rb +0 -28
- data/test/components/row_test.rb +0 -16
- data/test/components/table_for_test.rb +0 -211
- data/test/expander_stack_test.rb +0 -41
- data/test/expander_test.rb +0 -99
- data/test/markup/tag_test.rb +0 -149
- data/test/markup/wrapper_test.rb +0 -42
- data/test/markup/yielder_test.rb +0 -9
@@ -1,54 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
module Capabilities
|
4
|
-
# Adds the capability for a component to only render
|
5
|
-
# its markup when a condition to be evaluated in the
|
6
|
-
# view is true.
|
7
|
-
#
|
8
|
-
# Example:
|
9
|
-
#
|
10
|
-
# class PageHeader < ExpressTemplates::Components::Base
|
11
|
-
# include ExpressTemplates::Components::Capabilities::Conditionality
|
12
|
-
#
|
13
|
-
# emits {
|
14
|
-
# h1 { content_for(:page_header) }
|
15
|
-
# }
|
16
|
-
#
|
17
|
-
# only_if -> { content_for?(:page_header) }
|
18
|
-
#
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# The condition supplied to only if in the form of a proc
|
23
|
-
# is evaluated in the view context.
|
24
|
-
#
|
25
|
-
# The component will render an empty string if the proc returns false.
|
26
|
-
module Conditionality
|
27
|
-
def self.included(base)
|
28
|
-
base.class_eval do
|
29
|
-
extend ClassMethods
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
module ClassMethods
|
34
|
-
|
35
|
-
def condition_proc
|
36
|
-
@condition_proc
|
37
|
-
end
|
38
|
-
|
39
|
-
def only_if condition_proc
|
40
|
-
@condition_proc = Proc.from_source "-> {!(#{condition_proc.source_body})}"
|
41
|
-
inner_src = self[:markup]
|
42
|
-
fragment_src = %Q(-> {
|
43
|
-
unless_block(Proc.from_source(#{@condition_proc.source.inspect})) {
|
44
|
-
#{inner_src.source_body}
|
45
|
-
}
|
46
|
-
})
|
47
|
-
_store :markup, Proc.from_source(fragment_src)
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
module Capabilities
|
4
|
-
|
5
|
-
# Configurable components accept options which they can use to alter
|
6
|
-
# their markup each time they are invoked within a template.
|
7
|
-
#
|
8
|
-
# They do not compile their markup fragments at load time as simpler
|
9
|
-
# components do for efficiency. Rather they compile their fragments
|
10
|
-
# when they are themselves undergoing compilation. This facilitates
|
11
|
-
# access to arguments which were passed to the component at initialization.
|
12
|
-
#
|
13
|
-
# For example, if we have a Row component that is Configurable:
|
14
|
-
#
|
15
|
-
# row(:main)
|
16
|
-
#
|
17
|
-
# might process to:
|
18
|
-
#
|
19
|
-
# <div id="main" class="row" />
|
20
|
-
#
|
21
|
-
|
22
|
-
module Configurable
|
23
|
-
def self.included(base)
|
24
|
-
base.class_eval do
|
25
|
-
extend ClassMethods
|
26
|
-
include InstanceMethods
|
27
|
-
|
28
|
-
# Stores arguments for later processing, eg., compile time
|
29
|
-
def initialize(*args)
|
30
|
-
@args = args.dup
|
31
|
-
_process_args!(args)
|
32
|
-
super(*args)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
module ClassMethods
|
38
|
-
|
39
|
-
protected
|
40
|
-
|
41
|
-
# Override Templating._compile_fragment to delay compilation
|
42
|
-
def _compile_fragment(block, options = {})
|
43
|
-
if options.delete(:force_compile)
|
44
|
-
super(block, options)
|
45
|
-
else
|
46
|
-
block
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def _lookup(name, options = {})
|
51
|
-
super(name, options.merge(force_compile: true))
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
module InstanceMethods
|
57
|
-
|
58
|
-
def config
|
59
|
-
@config ||= {}
|
60
|
-
end
|
61
|
-
|
62
|
-
alias :my :config
|
63
|
-
|
64
|
-
def expand_locals
|
65
|
-
{my: config}
|
66
|
-
end
|
67
|
-
|
68
|
-
# Override Templating#lookup to pass locals
|
69
|
-
def lookup(fragment_name, options = {})
|
70
|
-
super(fragment_name, options.merge(expand_locals))
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
def _process_args!(args)
|
77
|
-
if args.first.kind_of?(Symbol)
|
78
|
-
config.merge!(id: args.shift)
|
79
|
-
end
|
80
|
-
while arg = args.shift
|
81
|
-
if arg.kind_of?(Hash)
|
82
|
-
config.merge!(arg)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
module Capabilities
|
4
|
-
#
|
5
|
-
# Adds the capability to iterate over a collection repeating a markup
|
6
|
-
# fragment for each member.
|
7
|
-
#
|
8
|
-
# Example:
|
9
|
-
#
|
10
|
-
# class ParagraphsComponent < ExpressTemplates::Components::Base
|
11
|
-
#
|
12
|
-
# emits -> { p { item } } # item is the default local variable name
|
13
|
-
#
|
14
|
-
# for_each -> { paragraphs } # evaluated in view context
|
15
|
-
#
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# Must specify an <tt>iterator</tt> either as a proc to be evaluated in the
|
19
|
-
# view context or else as a variable name in the form of a symbol which is
|
20
|
-
# assumed to be available in the view context.
|
21
|
-
#
|
22
|
-
# Provides:
|
23
|
-
#
|
24
|
-
# * Iterating::ClassMethods (for_each)
|
25
|
-
#
|
26
|
-
module Iterating
|
27
|
-
def self.included(base)
|
28
|
-
base.class_eval do
|
29
|
-
extend ClassMethods
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
module ClassMethods
|
34
|
-
# Sets the component up to use iterating logic to reproduce a fragment
|
35
|
-
# for a collection.
|
36
|
-
#
|
37
|
-
# Parameters include an iterator that may be :@variable assumed to be
|
38
|
-
# available in context or a proc that when evaluated in the context
|
39
|
-
# should return a collection.
|
40
|
-
#
|
41
|
-
# An <tt>:emit</tt> option may specify a fragment to emit.
|
42
|
-
# Defaults to <tt>:markup</tt>
|
43
|
-
#
|
44
|
-
# An <tt>:as</tt> option specifies the local variable name for each
|
45
|
-
# item in the collection for use in the fragment. Defaults to: <tt>item</tt>
|
46
|
-
#
|
47
|
-
# An <tt>:empty</tt> option specifies a fragment to use for the
|
48
|
-
# empty state when the iterator returns an empty collection.
|
49
|
-
def for_each(iterator, as: :item, emit: :markup, empty: nil)
|
50
|
-
as = as.to_sym
|
51
|
-
emit = emit.to_sym
|
52
|
-
iterator = iterator.kind_of?(Proc) ? iterator.source : iterator
|
53
|
-
fragment_src = if empty
|
54
|
-
%Q(-> {
|
55
|
-
unless_block(Proc.from_source("-> {#{iterator}.call.empty?}"), alt: #{self[empty].source}) {
|
56
|
-
for_each(Proc.from_source("#{iterator}"), as: #{as.inspect}) {
|
57
|
-
#{self[emit].source_body}
|
58
|
-
}
|
59
|
-
}
|
60
|
-
})
|
61
|
-
else
|
62
|
-
%Q(-> {
|
63
|
-
for_each(Proc.from_source("#{iterator}"), as: #{as.inspect}) {
|
64
|
-
#{self[emit].source_body}
|
65
|
-
}
|
66
|
-
})
|
67
|
-
end
|
68
|
-
fragment = Proc.from_source(fragment_src)
|
69
|
-
_store :markup, fragment
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
module Capabilities
|
4
|
-
module Parenting
|
5
|
-
|
6
|
-
# Parenting adds the capability for a component to have and render
|
7
|
-
# children that may be specified in the template fragment which
|
8
|
-
# includes the component.
|
9
|
-
#
|
10
|
-
# Example parenting component:
|
11
|
-
#
|
12
|
-
# class Line < ExpressTemplates::Components::Base
|
13
|
-
# include Capabilities::Parenting
|
14
|
-
#
|
15
|
-
# emits { p.line { _yield } }
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# You might then use this component like so:
|
19
|
-
#
|
20
|
-
# line "In", "Xanadu", "did", "Kubla", "Khan"
|
21
|
-
# line { "A stately pleasure-dome decree :" }
|
22
|
-
# line { "Where Alph, the sacred river, ran" }
|
23
|
-
# line %q(Through caverns measureless to man)
|
24
|
-
# line %q|Down to a sunless sea.|
|
25
|
-
#
|
26
|
-
# Provides
|
27
|
-
#
|
28
|
-
# * ClassMethods for rendering
|
29
|
-
# * InstanceMethods for managing children
|
30
|
-
#
|
31
|
-
def self.included(base)
|
32
|
-
base.class_eval do
|
33
|
-
include InstanceMethods
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
module InstanceMethods
|
38
|
-
def children
|
39
|
-
@children ||= []
|
40
|
-
end
|
41
|
-
|
42
|
-
def children=(children)
|
43
|
-
@children = children
|
44
|
-
children.each do |child|
|
45
|
-
if child.kind_of?(Adoptable)
|
46
|
-
child.parent = self
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def compile
|
52
|
-
null_wrapped_children = "null_wrap(%q(#{compile_children}))"
|
53
|
-
wrap_children_src = self.class[:markup].source.gsub(/(\s)_yield(\s)/, '\1'+null_wrapped_children+'\2')
|
54
|
-
_compile_fragment(Proc.from_source(wrap_children_src))
|
55
|
-
end
|
56
|
-
|
57
|
-
def compile_children
|
58
|
-
compiled_children = nil
|
59
|
-
Indenter.for(:compile) do |indent, indent_with_newline|
|
60
|
-
compiled_children = children.map do |child|
|
61
|
-
indent_with_newline +
|
62
|
-
(child.respond_to?(:compile) ? child.compile : child.inspect) # Bare strings may be children
|
63
|
-
end.join("+\n")
|
64
|
-
compiled_children.gsub!('"+"', '') # avoid unnecessary string concatenation
|
65
|
-
end
|
66
|
-
return compiled_children
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
module Capabilities
|
4
|
-
|
5
|
-
# Adds the capability for a component to render itself in a context.
|
6
|
-
#
|
7
|
-
# Provides both:
|
8
|
-
#
|
9
|
-
# * Rendering::ClassMethods
|
10
|
-
# * Rendering::InstanceMethods
|
11
|
-
#
|
12
|
-
# Used in ExpressTemplates::Components::Base.
|
13
|
-
#
|
14
|
-
module Rendering
|
15
|
-
def self.included(base)
|
16
|
-
base.class_eval do
|
17
|
-
extend ClassMethods
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
module ClassMethods
|
22
|
-
def render_in(context, &view_code)
|
23
|
-
context.instance_eval(&view_code)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,198 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
module Capabilities
|
4
|
-
|
5
|
-
# The Templating capability module provides Components with the ability
|
6
|
-
# to store, reference and compile template fragments.
|
7
|
-
#
|
8
|
-
# It extends the including class with Templating::ClassMethods.
|
9
|
-
#
|
10
|
-
# It also provides helpers which are snippets of code in the form of
|
11
|
-
# lambdas that may be evaluated in the view context.
|
12
|
-
#
|
13
|
-
module Templating
|
14
|
-
def self.included(base)
|
15
|
-
base.class_eval do
|
16
|
-
extend ClassMethods
|
17
|
-
include InstanceMethods
|
18
|
-
end
|
19
|
-
class << base
|
20
|
-
alias_method :fragments, :emits
|
21
|
-
alias_method :has_markup, :emits
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module ClassMethods
|
26
|
-
|
27
|
-
# Store fragments of ExpressTemplate style markup for use in
|
28
|
-
# generating the HTML representation of a component.
|
29
|
-
#
|
30
|
-
# For example in your class, simply place the following:
|
31
|
-
#
|
32
|
-
# class MyComponent < ET::Components::Base
|
33
|
-
# emits {
|
34
|
-
# ul {
|
35
|
-
# li "one"
|
36
|
-
# li "two"
|
37
|
-
# li "three"
|
38
|
-
# }
|
39
|
-
# }
|
40
|
-
#
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# By default this template code is stored under the label :markup
|
44
|
-
#
|
45
|
-
# You may specify several fragments with a hash containing lambdas:
|
46
|
-
#
|
47
|
-
# emits body: -> { li "item" },
|
48
|
-
# wrapper: -> { ul { _yield } }
|
49
|
-
#
|
50
|
-
# This method is aliased as <tt>fragments</tt> and <tt>has_markup</tt>
|
51
|
-
#
|
52
|
-
def emits(*args, &template_code)
|
53
|
-
if args.first.respond_to?(:call) or template_code
|
54
|
-
fragment = (args.first||template_code)
|
55
|
-
raise "must use stabby lambda" unless fragment.lambda?
|
56
|
-
_store :markup, fragment# default fragment is named :markup
|
57
|
-
else
|
58
|
-
args.first.to_a.each do |name, block|
|
59
|
-
raise ArgumentError unless name.is_a?(Symbol) and block.is_a?(Proc)
|
60
|
-
_store(name, block)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def [](label)
|
66
|
-
_lookup(label)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Stores a block given for later evaluation in context.
|
70
|
-
#
|
71
|
-
# Example:
|
72
|
-
#
|
73
|
-
# class TitleComponent < ECB
|
74
|
-
# helper :title_helper do
|
75
|
-
# @resource.name
|
76
|
-
# end
|
77
|
-
#
|
78
|
-
# emits {
|
79
|
-
# h1 {
|
80
|
-
# title_helper
|
81
|
-
# }
|
82
|
-
# }
|
83
|
-
#
|
84
|
-
# end
|
85
|
-
#
|
86
|
-
# In this example <tt>@resource.name</tt> is evaluated in the
|
87
|
-
# provided context during page rendering and not during template
|
88
|
-
# expansion or compilation.
|
89
|
-
#
|
90
|
-
# This is the recommended for encapsulation of "helper" type
|
91
|
-
# functionality which is of concern only to the component and
|
92
|
-
# used only in its own markup fragments.
|
93
|
-
def helper(name, &block)
|
94
|
-
_helpers[name] = block
|
95
|
-
_define_helper_methods name
|
96
|
-
end
|
97
|
-
|
98
|
-
def special_handlers
|
99
|
-
{insert: self, _yield: self}.merge(Hash[*(_helpers.keys.map {|k| [k, self] }.flatten)])
|
100
|
-
end
|
101
|
-
|
102
|
-
protected
|
103
|
-
|
104
|
-
# Stores a fragment for use during compilation and rendering
|
105
|
-
# of a component.
|
106
|
-
def _store(name, fragment)
|
107
|
-
@fragments ||= Hash.new
|
108
|
-
@fragments[name] = fragment
|
109
|
-
end
|
110
|
-
|
111
|
-
# Looks up a template fragment for this component and returns
|
112
|
-
# compiled template code.
|
113
|
-
#
|
114
|
-
# If the template fragment is not already compiled, it compiles it
|
115
|
-
# with the supplied options as locals. Locals may be used within
|
116
|
-
# the template during expansion.
|
117
|
-
#
|
118
|
-
# Returns a string containing ruby code which evaluates to markup.
|
119
|
-
def _lookup(name, options = {})
|
120
|
-
@fragments ||= Hash.new
|
121
|
-
@fragments[name] or raise "no template fragment supplied for: #{name}"
|
122
|
-
end
|
123
|
-
|
124
|
-
private
|
125
|
-
|
126
|
-
|
127
|
-
def _helpers
|
128
|
-
@helpers ||= Hash.new
|
129
|
-
end
|
130
|
-
|
131
|
-
def _define_helper_methods(name)
|
132
|
-
method_definition= <<-RUBY
|
133
|
-
class << self
|
134
|
-
|
135
|
-
# called during expansion
|
136
|
-
define_method(:#{name}) do |*args|
|
137
|
-
helper_args = %w(self)
|
138
|
-
helper_args += args.map(&:inspect)
|
139
|
-
'{{#{self.to_s}._#{name}('+_interpolate(helper_args).join(', ')+')}}'
|
140
|
-
end
|
141
|
-
|
142
|
-
# called during rendering in view context
|
143
|
-
define_method(:_#{name}) do |context, *args|
|
144
|
-
begin
|
145
|
-
helper_proc = _helpers[:#{name}]
|
146
|
-
helper_args = args.take(helper_proc.arity)
|
147
|
-
context.instance_exec *helper_args, &helper_proc
|
148
|
-
rescue => e
|
149
|
-
raise "#{name} raised: \#\{e.to_s\}"
|
150
|
-
end.to_s
|
151
|
-
end
|
152
|
-
end
|
153
|
-
RUBY
|
154
|
-
eval(method_definition)
|
155
|
-
end
|
156
|
-
|
157
|
-
def _interpolate(args)
|
158
|
-
args.map do |arg|
|
159
|
-
if arg.kind_of?(String) && match = arg.match(/"\{\{(.*)\}\}"/)
|
160
|
-
match[1]
|
161
|
-
else
|
162
|
-
arg
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
module InstanceMethods
|
170
|
-
|
171
|
-
def lookup(fragment_name, options={})
|
172
|
-
fragment = self.class[fragment_name]
|
173
|
-
if fragment.kind_of?(Proc)
|
174
|
-
_compile_fragment(fragment, options)
|
175
|
-
else
|
176
|
-
fragment
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
# Expands and compiles the supplied block representing a
|
181
|
-
# template fragment.
|
182
|
-
#
|
183
|
-
# Any supplied options are passed as locals for use during expansion.
|
184
|
-
#
|
185
|
-
# Returns a string containing ruby code which evaluates to markup.
|
186
|
-
def _compile_fragment(block, options = {})
|
187
|
-
initialize_expander(nil, self.class.special_handlers, options)
|
188
|
-
expand(&block).map(&:compile).join("+").gsub('"+"', '')
|
189
|
-
end
|
190
|
-
|
191
|
-
|
192
|
-
end
|
193
|
-
|
194
|
-
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
module Capabilities
|
4
|
-
|
5
|
-
# Add the ability for a component template to wrap or decorate a fragment
|
6
|
-
# with another fragment.
|
7
|
-
#
|
8
|
-
# The insertion point for the inner fragment is marked with <tt>_yield</tt>
|
9
|
-
#
|
10
|
-
# Example:
|
11
|
-
#
|
12
|
-
# class MenuComponent < ExpressTemplates::Components::Base
|
13
|
-
#
|
14
|
-
# fragments :menu_item, -> { li { menu_link(item) } },
|
15
|
-
# :menu_wrapper, -> { ul { _yield } }
|
16
|
-
#
|
17
|
-
# for_each -> { menu_items }
|
18
|
-
#
|
19
|
-
# wrap_with :menu_wrapper
|
20
|
-
#
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# Note this example also uses Capabilities::Iterating.
|
24
|
-
#
|
25
|
-
# Provides:
|
26
|
-
#
|
27
|
-
# * Wrapping::ClassMethods
|
28
|
-
#
|
29
|
-
module Wrapping
|
30
|
-
def self.included(base)
|
31
|
-
base.class_eval do
|
32
|
-
extend ClassMethods
|
33
|
-
include InstanceMethods
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
module InstanceMethods
|
38
|
-
def compile
|
39
|
-
lookup :markup
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
module ClassMethods
|
44
|
-
|
45
|
-
# Enclose whatever the component would already generate
|
46
|
-
# inside the specified fragment wherever we encounter _yield
|
47
|
-
def wrap_with(fragment, dont_wrap_if: false )
|
48
|
-
wrapper_name(fragment)
|
49
|
-
wrapper_src = _lookup(fragment).source
|
50
|
-
inner_src = _lookup(:markup).source_body
|
51
|
-
wrapped_src = wrapper_src.gsub!(/\W_yield\W/, inner_src)
|
52
|
-
|
53
|
-
fragment_src = if dont_wrap_if
|
54
|
-
%Q(-> {
|
55
|
-
unless_block(Proc.from_source(#{dont_wrap_if.source.inspect}), alt: Proc.from_source(%q(-> {#{inner_src}}))) {
|
56
|
-
#{Proc.from_source(wrapped_src).source_body}
|
57
|
-
}
|
58
|
-
})
|
59
|
-
else
|
60
|
-
wrapped_src
|
61
|
-
end
|
62
|
-
|
63
|
-
_store :markup, Proc.from_source(fragment_src)
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
def wrapper_name(name = nil)
|
68
|
-
if name.nil?
|
69
|
-
@wrapper_name || :markup
|
70
|
-
else
|
71
|
-
@wrapper_name = name
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def _yield(*args)
|
76
|
-
"{{_yield}}"
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
# Provide a wrapper for the content_for helper which
|
4
|
-
# accepts a block of express template code.
|
5
|
-
#
|
6
|
-
# Example:
|
7
|
-
#
|
8
|
-
# ```ruby
|
9
|
-
# content_for(:header) {
|
10
|
-
# h1 "Title"
|
11
|
-
# }
|
12
|
-
# ```
|
13
|
-
# Or:
|
14
|
-
# ```ruby
|
15
|
-
# content_for(:page_title), "People"
|
16
|
-
# ```
|
17
|
-
class ContentFor < Container
|
18
|
-
include Capabilities::Configurable
|
19
|
-
def compile
|
20
|
-
children_markup = compile_children
|
21
|
-
content_label = @args[0]
|
22
|
-
result = %Q|\ncontent_for(:#{content_label}|
|
23
|
-
if children_markup.empty?
|
24
|
-
if @args[1].kind_of?(String)
|
25
|
-
children_markup = @args[1]
|
26
|
-
# append children as argument
|
27
|
-
result << %Q|, "#{children_markup}".html_safe).to_s|
|
28
|
-
else
|
29
|
-
# no markup specified - must be referencing the content
|
30
|
-
result << ").to_s"
|
31
|
-
end
|
32
|
-
else
|
33
|
-
# append children in block form
|
34
|
-
result << %Q|) {
|
35
|
-
(#{children_markup.gsub(/^\s+/, '')}).html_safe
|
36
|
-
}.to_s|
|
37
|
-
end
|
38
|
-
result
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module ExpressTemplates
|
2
|
-
module Components
|
3
|
-
class ForEach < Components::Container
|
4
|
-
attr :collection, :member
|
5
|
-
|
6
|
-
def initialize(*args)
|
7
|
-
iterator = args.shift
|
8
|
-
options = args.first.kind_of?(Hash) ? args.shift : {}
|
9
|
-
expander = args.shift
|
10
|
-
@collection, @member = nil, (options[:as]||"item")
|
11
|
-
if iterator.kind_of?(Symbol)
|
12
|
-
@collection = iterator.to_s
|
13
|
-
@member = collection.sub(/^@/, '').singularize
|
14
|
-
elsif iterator.kind_of?(Proc)
|
15
|
-
@collection = "(#{iterator.source}.call)"
|
16
|
-
elsif iterator.kind_of?(String)
|
17
|
-
@collection = "(#{iterator}.call)"
|
18
|
-
else
|
19
|
-
raise "ForEach unknown iterator: #{iterator}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
def compile
|
25
|
-
%Q((#{@collection}.each_with_index.map do |#{@member}, #{@member}_index|#{compile_children}\nend).join)
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|