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.
Files changed (58) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +4 -4
  3. data/.travis.yml +51 -0
  4. data/Gemfile +15 -2
  5. data/Guardfile +15 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +41 -0
  8. data/Rakefile +3 -7
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/blocks.gemspec +18 -11
  12. data/gemfiles/Gemfile.rails-3-0-stable +13 -0
  13. data/gemfiles/Gemfile.rails-3-1-stable +13 -0
  14. data/gemfiles/Gemfile.rails-3-2-stable +13 -0
  15. data/gemfiles/Gemfile.rails-4-0-stable +12 -0
  16. data/gemfiles/Gemfile.rails-4-1-stable +12 -0
  17. data/gemfiles/Gemfile.rails-4-2-stable +12 -0
  18. data/gemfiles/Gemfile.rails-5-0-stable +10 -0
  19. data/gemfiles/Gemfile.rails-5-1-stable +10 -0
  20. data/lib/blocks.rb +41 -20
  21. data/lib/blocks/action_view_extensions/view_extensions.rb +26 -0
  22. data/lib/blocks/builders/block_definition.rb +71 -0
  23. data/lib/blocks/builders/builder.rb +159 -0
  24. data/lib/blocks/builders/hook_definition.rb +25 -0
  25. data/lib/blocks/experimental/builder_permissions.rb +52 -0
  26. data/lib/blocks/experimental/invalid_permissions_handler.rb +27 -0
  27. data/lib/blocks/renderers/abstract_renderer.rb +69 -0
  28. data/lib/blocks/renderers/adjacent_blocks_renderer.rb +15 -0
  29. data/lib/blocks/renderers/block_placeholder.rb +13 -0
  30. data/lib/blocks/renderers/block_renderer.rb +13 -0
  31. data/lib/blocks/renderers/block_with_hooks_renderer.rb +35 -0
  32. data/lib/blocks/renderers/collection_renderer.rb +17 -0
  33. data/lib/blocks/renderers/nesting_blocks_renderer.rb +24 -0
  34. data/lib/blocks/renderers/partial_renderer.rb +18 -0
  35. data/lib/blocks/renderers/renderer.rb +43 -0
  36. data/lib/blocks/renderers/runtime_context.rb +193 -0
  37. data/lib/blocks/renderers/wrapper_renderer.rb +19 -0
  38. data/lib/blocks/utilities/configurator.rb +26 -0
  39. data/lib/blocks/utilities/dynamic_configuration.rb +71 -0
  40. data/lib/blocks/utilities/hash_with_caller.rb +73 -0
  41. data/lib/blocks/utilities/hash_with_render_strategy.rb +47 -0
  42. data/lib/blocks/utilities/options_set.rb +95 -0
  43. data/lib/blocks/version.rb +1 -1
  44. metadata +70 -80
  45. data/.ruby-gemset +0 -1
  46. data/.ruby-version +0 -1
  47. data/README.rdoc +0 -99
  48. data/blocks_render_order.graffle +0 -1397
  49. data/blocks_render_order.png +0 -0
  50. data/lib/blocks/base.rb +0 -580
  51. data/lib/blocks/container.rb +0 -5
  52. data/lib/blocks/controller_additions.rb +0 -9
  53. data/lib/blocks/view_additions.rb +0 -10
  54. data/rails/init.rb +0 -1
  55. data/spec/blocks/base_spec.rb +0 -641
  56. data/spec/blocks/blocks_spec.rb +0 -12
  57. data/spec/blocks/view_additions_spec.rb +0 -22
  58. data/spec/spec_helper.rb +0 -22
@@ -0,0 +1,13 @@
1
+ module Blocks
2
+ class BlockPlaceholder
3
+ attr_accessor :block_definition
4
+
5
+ def initialize(block_definition)
6
+ self.block_definition = block_definition
7
+ end
8
+
9
+ def to_s
10
+ "PLACEHOLDER_FOR_#{block_definition.name} "
11
+ end
12
+ end
13
+ end
@@ -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