blocks 2.8.0 → 3.0.0.rc1
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 +5 -13
- data/.gitignore +4 -4
- data/.travis.yml +51 -0
- data/Gemfile +15 -2
- data/Guardfile +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +3 -7
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/blocks.gemspec +18 -11
- data/gemfiles/Gemfile.rails-3-0-stable +13 -0
- data/gemfiles/Gemfile.rails-3-1-stable +13 -0
- data/gemfiles/Gemfile.rails-3-2-stable +13 -0
- data/gemfiles/Gemfile.rails-4-0-stable +12 -0
- data/gemfiles/Gemfile.rails-4-1-stable +12 -0
- data/gemfiles/Gemfile.rails-4-2-stable +12 -0
- data/gemfiles/Gemfile.rails-5-0-stable +10 -0
- data/gemfiles/Gemfile.rails-5-1-stable +10 -0
- data/lib/blocks.rb +41 -20
- data/lib/blocks/action_view_extensions/view_extensions.rb +26 -0
- data/lib/blocks/builders/block_definition.rb +71 -0
- data/lib/blocks/builders/builder.rb +159 -0
- data/lib/blocks/builders/hook_definition.rb +25 -0
- data/lib/blocks/experimental/builder_permissions.rb +52 -0
- data/lib/blocks/experimental/invalid_permissions_handler.rb +27 -0
- data/lib/blocks/renderers/abstract_renderer.rb +69 -0
- data/lib/blocks/renderers/adjacent_blocks_renderer.rb +15 -0
- data/lib/blocks/renderers/block_placeholder.rb +13 -0
- data/lib/blocks/renderers/block_renderer.rb +13 -0
- data/lib/blocks/renderers/block_with_hooks_renderer.rb +35 -0
- data/lib/blocks/renderers/collection_renderer.rb +17 -0
- data/lib/blocks/renderers/nesting_blocks_renderer.rb +24 -0
- data/lib/blocks/renderers/partial_renderer.rb +18 -0
- data/lib/blocks/renderers/renderer.rb +43 -0
- data/lib/blocks/renderers/runtime_context.rb +193 -0
- data/lib/blocks/renderers/wrapper_renderer.rb +19 -0
- data/lib/blocks/utilities/configurator.rb +26 -0
- data/lib/blocks/utilities/dynamic_configuration.rb +71 -0
- data/lib/blocks/utilities/hash_with_caller.rb +73 -0
- data/lib/blocks/utilities/hash_with_render_strategy.rb +47 -0
- data/lib/blocks/utilities/options_set.rb +95 -0
- data/lib/blocks/version.rb +1 -1
- metadata +70 -80
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/README.rdoc +0 -99
- data/blocks_render_order.graffle +0 -1397
- data/blocks_render_order.png +0 -0
- data/lib/blocks/base.rb +0 -580
- data/lib/blocks/container.rb +0 -5
- data/lib/blocks/controller_additions.rb +0 -9
- data/lib/blocks/view_additions.rb +0 -10
- data/rails/init.rb +0 -1
- data/spec/blocks/base_spec.rb +0 -641
- data/spec/blocks/blocks_spec.rb +0 -12
- data/spec/blocks/view_additions_spec.rb +0 -22
- data/spec/spec_helper.rb +0 -22
@@ -0,0 +1,13 @@
|
|
1
|
+
module Blocks
|
2
|
+
class BlockRenderer < AbstractRenderer
|
3
|
+
def render(*args, runtime_context)
|
4
|
+
render_item = runtime_context.render_item
|
5
|
+
if render_item.is_a?(String)
|
6
|
+
output_buffer << partial_renderer.render(render_item, runtime_context)
|
7
|
+
elsif render_item.is_a?(Proc)
|
8
|
+
args = args + runtime_context.runtime_args
|
9
|
+
output_buffer << capture(*args, runtime_context, &render_item)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Blocks
|
2
|
+
# TODO: Make this render order customizable
|
3
|
+
class BlockWithHooksRenderer < AbstractRenderer
|
4
|
+
def render(*args, &default_definition)
|
5
|
+
with_output_buffer do
|
6
|
+
runtime_context = RuntimeContext.new(builder, *args, &default_definition)
|
7
|
+
if !runtime_context.skip_completely
|
8
|
+
adjacent_blocks_renderer.render(HookDefinition::BEFORE_ALL, runtime_context)
|
9
|
+
nesting_blocks_renderer.render(HookDefinition::AROUND_ALL, runtime_context) do
|
10
|
+
wrapper_renderer.render(runtime_context.wrap_all, runtime_context) do
|
11
|
+
collection_renderer.render(runtime_context) do |runtime_context|
|
12
|
+
wrapper_renderer.render(runtime_context.wrap_each, runtime_context) do
|
13
|
+
nesting_blocks_renderer.render(HookDefinition::AROUND, runtime_context) do
|
14
|
+
adjacent_blocks_renderer.render(HookDefinition::BEFORE, runtime_context)
|
15
|
+
if !runtime_context.skip_content
|
16
|
+
wrapper_renderer.render(runtime_context.wrap_with, runtime_context) do
|
17
|
+
nesting_blocks_renderer.render(HookDefinition::SURROUND, runtime_context) do
|
18
|
+
adjacent_blocks_renderer.render(HookDefinition::PREPEND, runtime_context)
|
19
|
+
block_renderer.render(runtime_context)
|
20
|
+
adjacent_blocks_renderer.render(HookDefinition::APPEND, runtime_context)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
adjacent_blocks_renderer.render(HookDefinition::AFTER, runtime_context)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
adjacent_blocks_renderer.render(HookDefinition::AFTER_ALL, runtime_context)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Blocks
|
2
|
+
class CollectionRenderer < AbstractRenderer
|
3
|
+
def render(runtime_context)
|
4
|
+
collection = runtime_context.collection
|
5
|
+
if collection
|
6
|
+
object_name = runtime_context.as || :object
|
7
|
+
collection.each_with_index do |item, index|
|
8
|
+
item_runtime_context = runtime_context.merge(object_name => item, current_index: index)
|
9
|
+
item_runtime_context.runtime_args = [item] + item_runtime_context.runtime_args
|
10
|
+
yield item_runtime_context
|
11
|
+
end
|
12
|
+
else
|
13
|
+
yield runtime_context
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Blocks
|
2
|
+
class NestingBlocksRenderer < AbstractRenderer
|
3
|
+
def render(hook, runtime_context, &block)
|
4
|
+
block = block_for(runtime_context.block_name)
|
5
|
+
if block
|
6
|
+
content_block = Proc.new { with_output_buffer { yield } }
|
7
|
+
hooks = block.hooks_for(hook)
|
8
|
+
|
9
|
+
renderer = hooks.inject(content_block) do |inner_content, block_definition|
|
10
|
+
hook_runtime_context = runtime_context.extend_to_block_definition(block_definition)
|
11
|
+
Proc.new {
|
12
|
+
with_output_buffer do
|
13
|
+
block_renderer.render(inner_content, hook_runtime_context)
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
output_buffer << renderer.call
|
19
|
+
else
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Blocks
|
2
|
+
class PartialRenderer < AbstractRenderer
|
3
|
+
def render(partial, options={}, &block)
|
4
|
+
overrides_and_provided_content = capture(builder, &block) if block_given?
|
5
|
+
locals = options.merge(
|
6
|
+
(options[:builder_variable] || :builder) => builder,
|
7
|
+
).symbolize_keys
|
8
|
+
locals[:options] = options
|
9
|
+
view.render(layout: partial, locals: locals) do |*args|
|
10
|
+
if overrides_and_provided_content
|
11
|
+
overrides_and_provided_content.to_str.gsub(/PLACEHOLDER_FOR_([\w]+)/) do |s|
|
12
|
+
builder.render $1, *args
|
13
|
+
end.html_safe
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Blocks
|
2
|
+
class Renderer
|
3
|
+
attr_accessor :builder
|
4
|
+
|
5
|
+
def initialize(builder)
|
6
|
+
self.builder = builder
|
7
|
+
end
|
8
|
+
|
9
|
+
def render(*args, &block)
|
10
|
+
block_with_hooks_renderer.render(*args, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO: this needs to be handled by a new renderer
|
14
|
+
def render_with_overrides(*args, &block)
|
15
|
+
options = args.extract_options!
|
16
|
+
name = args.first
|
17
|
+
if name.is_a?(Symbol) || name.is_a?(String)
|
18
|
+
block_with_hooks_renderer.render(*args, options, &block)
|
19
|
+
elsif options[:partial]
|
20
|
+
partial_renderer.render(options.delete(:partial), options, &block)
|
21
|
+
else
|
22
|
+
# TODO: handle other possible rendering methods such as a custom renderer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO: this needs to be handled by a new renderer
|
27
|
+
# TODO: also get rid of BlockPlaceholder
|
28
|
+
def deferred_render(*args, &block)
|
29
|
+
block_definition = builder.define(*args, &block)
|
30
|
+
Blocks::BlockPlaceholder.new(block_definition)
|
31
|
+
end
|
32
|
+
|
33
|
+
AbstractRenderer::RENDERERS.each do |klass|
|
34
|
+
name = klass.to_s.demodulize.underscore
|
35
|
+
|
36
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
37
|
+
def #{name}
|
38
|
+
@#{name} ||= #{klass}.new(self)
|
39
|
+
end
|
40
|
+
RUBY
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Blocks
|
2
|
+
class RuntimeContext < HashWithRenderStrategy
|
3
|
+
CONTROL_VARIABLES = {
|
4
|
+
wrap_all: [],
|
5
|
+
wrap_each: [:outer_wrapper],
|
6
|
+
wrap_with: [:wrap, :wrapper, :inner_wrapper],
|
7
|
+
collection: [],
|
8
|
+
as: []
|
9
|
+
}
|
10
|
+
|
11
|
+
attr_accessor(*CONTROL_VARIABLES.keys)
|
12
|
+
attr_accessor :block_name,
|
13
|
+
:runtime_block,
|
14
|
+
:builder,
|
15
|
+
:render_item,
|
16
|
+
:runtime_args,
|
17
|
+
:render_options_set,
|
18
|
+
:block_options_set,
|
19
|
+
:proxy_options_set,
|
20
|
+
:parent_options_set,
|
21
|
+
:merged_options_set
|
22
|
+
|
23
|
+
delegate :skip_content, :skip_completely, to: :block_options_set, allow_nil: true
|
24
|
+
|
25
|
+
delegate :block_definitions, :block_defined?, to: :builder
|
26
|
+
|
27
|
+
delegate :runtime_options,
|
28
|
+
:standard_options,
|
29
|
+
:default_options,
|
30
|
+
:options_set,
|
31
|
+
prefix: :builder,
|
32
|
+
to: :builder
|
33
|
+
|
34
|
+
def initialize(builder, *runtime_args, &runtime_block)
|
35
|
+
super(&nil)
|
36
|
+
|
37
|
+
self.builder = builder
|
38
|
+
self.runtime_block = runtime_block
|
39
|
+
self.proxy_options_set = OptionsSet.new("Proxy Options Set")
|
40
|
+
|
41
|
+
convert_render_options(runtime_args.extract_options!)
|
42
|
+
identify_block(runtime_args.shift)
|
43
|
+
|
44
|
+
self.runtime_args = runtime_args
|
45
|
+
merge_options_and_identify_render_item
|
46
|
+
extract_control_options
|
47
|
+
end
|
48
|
+
|
49
|
+
def extend_to_block_definition(block_definition)
|
50
|
+
RuntimeContext.new(builder, block_definition, parent_options_set: merged_options_set.clone).tap do |rc|
|
51
|
+
rc.runtime_args = self.runtime_args
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# TODO: this method needs to be rewritten to output a proper hash
|
56
|
+
def to_s
|
57
|
+
description = []
|
58
|
+
if block_name
|
59
|
+
block_name = self.block_name.to_s
|
60
|
+
if block_name.include?(" ")
|
61
|
+
block_name = ":\"#{block_name}\""
|
62
|
+
else
|
63
|
+
block_name = ":#{block_name}"
|
64
|
+
end
|
65
|
+
description << "Block Name: #{block_name}"
|
66
|
+
end
|
67
|
+
|
68
|
+
if render_item.is_a?(String)
|
69
|
+
description << "Renders with partial \"#{render_item}\""
|
70
|
+
elsif render_item.is_a?(Proc)
|
71
|
+
description << "Renders with block defined at #{render_item.source_location}"
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
CONTROL_VARIABLES.each do |control_variable, *|
|
76
|
+
if value = send(control_variable)
|
77
|
+
description << "#{control_variable}: #{value} [#{callers[control_variable]}]"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
description << super
|
82
|
+
description.join("\n")
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def convert_render_options(render_options)
|
88
|
+
if !render_options.is_a?(HashWithIndifferentAccess)
|
89
|
+
render_options = render_options.with_indifferent_access
|
90
|
+
end
|
91
|
+
self.parent_options_set = render_options.delete(:parent_options_set)
|
92
|
+
self.render_options_set = OptionsSet.new(
|
93
|
+
"Render Options",
|
94
|
+
defaults: render_options.delete(:defaults),
|
95
|
+
runtime: render_options
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def identify_block(block_identifier)
|
100
|
+
self.block_name, self.block_options_set = if block_identifier.is_a?(OptionsSet)
|
101
|
+
[block_identifier.name, block_identifier]
|
102
|
+
elsif block_defined?(block_identifier)
|
103
|
+
[block_identifier, block_definitions[block_identifier]]
|
104
|
+
elsif block_identifier.is_a?(Proc)
|
105
|
+
# TODO: figure out how to do this
|
106
|
+
else
|
107
|
+
[block_identifier, nil]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def add_proxy_options(proxy_block_name)
|
112
|
+
if block_defined?(proxy_block_name)
|
113
|
+
proxy_block = block_definitions[proxy_block_name]
|
114
|
+
|
115
|
+
proxy_options_set.add_options(proxy_block)
|
116
|
+
|
117
|
+
render_strategy, render_item = proxy_block.current_render_strategy_and_item
|
118
|
+
|
119
|
+
if render_strategy == RENDER_WITH_PROXY
|
120
|
+
add_proxy_options render_item
|
121
|
+
else
|
122
|
+
render_item
|
123
|
+
end
|
124
|
+
|
125
|
+
elsif builder.respond_to?(proxy_block_name)
|
126
|
+
Proc.new do |*args|
|
127
|
+
options = args.extract_options!
|
128
|
+
runtime_block = runtime_block || options[:block]
|
129
|
+
builder.send(proxy_block_name, *args, options, &runtime_block)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def merge_options_and_identify_render_item
|
135
|
+
determined_render_item = false
|
136
|
+
all_options_sets = [
|
137
|
+
render_options_set,
|
138
|
+
block_options_set,
|
139
|
+
OptionsSet.new("Runtime Block", block: self.runtime_block),
|
140
|
+
builder_options_set,
|
141
|
+
Blocks.global_options_set,
|
142
|
+
parent_options_set
|
143
|
+
].compact
|
144
|
+
|
145
|
+
options_set_with_render_strategy_index = nil
|
146
|
+
all_options_sets.
|
147
|
+
map(&:render_strategies_and_items).
|
148
|
+
transpose.
|
149
|
+
detect do |options_for_level|
|
150
|
+
options_set_with_render_strategy_index = options_for_level.index(&:present?)
|
151
|
+
if options_set_with_render_strategy_index.present?
|
152
|
+
self.render_strategy, self.render_item =
|
153
|
+
options_for_level[options_set_with_render_strategy_index]
|
154
|
+
true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
if self.render_strategy == RENDER_WITH_PROXY
|
159
|
+
self.render_item = add_proxy_options render_item
|
160
|
+
end
|
161
|
+
|
162
|
+
self.merged_options_set = OptionsSet.new("Parent Options Set")
|
163
|
+
[:runtime_options, :standard_options, :default_options].each do |option_level|
|
164
|
+
all_options_sets.each_with_index do |options_set, index|
|
165
|
+
options_for_level = options_set.send(option_level)
|
166
|
+
merged_options_set.send(option_level).add_options(options_for_level)
|
167
|
+
add_options options_for_level
|
168
|
+
|
169
|
+
if index == options_set_with_render_strategy_index
|
170
|
+
options_for_level = proxy_options_set.send(option_level)
|
171
|
+
merged_options_set.send(option_level).add_options(options_for_level)
|
172
|
+
add_options options_for_level
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
if render_item.blank?
|
178
|
+
self.render_item = runtime_block
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def extract_control_options
|
183
|
+
CONTROL_VARIABLES.each do |control_variable, synonyms|
|
184
|
+
variant = (Array(synonyms) + Array(control_variable)).detect {|variant| key?(variant)}
|
185
|
+
value = delete(variant)
|
186
|
+
callers[control_variable] = callers[variant] if value
|
187
|
+
self.send("#{control_variable}=", value)
|
188
|
+
end
|
189
|
+
|
190
|
+
RENDERING_STRATEGIES.each {|rs| delete(rs) }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Blocks
|
2
|
+
class WrapperRenderer < AbstractRenderer
|
3
|
+
def render(wrapper, runtime_context, &block)
|
4
|
+
content_block = Proc.new { with_output_buffer { yield } }
|
5
|
+
if wrapper.nil?
|
6
|
+
yield
|
7
|
+
elsif wrapper.is_a?(Proc)
|
8
|
+
output_buffer << capture(content_block, *(runtime_context.runtime_args), runtime_context, &wrapper)
|
9
|
+
elsif block_definition = block_for(wrapper)
|
10
|
+
runtime_context = runtime_context.extend_to_block_definition(block_definition)
|
11
|
+
block_renderer.render(content_block, runtime_context)
|
12
|
+
elsif builder.respond_to?(wrapper)
|
13
|
+
output_buffer << builder.send(wrapper, runtime_context, &content_block)
|
14
|
+
else
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Blocks
|
2
|
+
module Configurator
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
include DynamicConfiguration
|
7
|
+
|
8
|
+
add_config :builder_class
|
9
|
+
add_config :renderer_class
|
10
|
+
add_config :global_options_set
|
11
|
+
add_config :lookup_caller_location, instance_predicate: true
|
12
|
+
reset_config
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def reset_config
|
17
|
+
configure do |config|
|
18
|
+
config.builder_class = Builder
|
19
|
+
config.renderer_class = Renderer
|
20
|
+
config.lookup_caller_location = true
|
21
|
+
config.global_options_set = OptionsSet.new("Global Options")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Blocks
|
2
|
+
module DynamicConfiguration
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def add_config(*attrs)
|
7
|
+
options = attrs.extract_options!
|
8
|
+
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
|
9
|
+
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
|
10
|
+
instance_predicate = options.fetch(:instance_predicate, true)
|
11
|
+
default_value = options.fetch(:default, nil)
|
12
|
+
|
13
|
+
attrs.each do |name|
|
14
|
+
define_singleton_method(name) do |value="UNDEFINED"|
|
15
|
+
if value == "UNDEFINED"
|
16
|
+
default_value
|
17
|
+
else
|
18
|
+
public_send("#{name}=", value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
|
22
|
+
|
23
|
+
ivar = "@#{name}"
|
24
|
+
|
25
|
+
define_singleton_method("#{name}=") do |val|
|
26
|
+
singleton_class.class_eval do
|
27
|
+
remove_possible_method(name)
|
28
|
+
define_method(name) do |value="UNDEFINED"|
|
29
|
+
if value == "UNDEFINED"
|
30
|
+
val
|
31
|
+
else
|
32
|
+
public_send("#{name}=", value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class_eval do
|
38
|
+
remove_possible_method(name)
|
39
|
+
define_method(name) do
|
40
|
+
if instance_variable_defined? ivar
|
41
|
+
instance_variable_get ivar
|
42
|
+
else
|
43
|
+
singleton_class.send name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
val
|
48
|
+
end
|
49
|
+
|
50
|
+
if instance_reader
|
51
|
+
remove_possible_method name
|
52
|
+
define_method(name) do
|
53
|
+
if instance_variable_defined?(ivar)
|
54
|
+
instance_variable_get ivar
|
55
|
+
else
|
56
|
+
self.class.public_send name
|
57
|
+
end
|
58
|
+
end
|
59
|
+
define_method("#{name}?") { !!public_send(name) } if instance_predicate
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_writer name if instance_writer
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def configure
|
67
|
+
yield self
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|