isomorfeus-react 16.13.6 → 16.13.11

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -10
  3. data/lib/isomorfeus-react-native.rb +5 -0
  4. data/lib/isomorfeus-react-paper.rb +4 -0
  5. data/lib/isomorfeus-react.rb +9 -7
  6. data/lib/isomorfeus/props/validator.rb +2 -2
  7. data/lib/isomorfeus/{react_config.rb → react/config.rb} +1 -1
  8. data/lib/isomorfeus/react/memcached_component_cache.rb +19 -0
  9. data/lib/isomorfeus/react/redis_component_cache.rb +19 -0
  10. data/lib/isomorfeus/{thread_local_component_cache.rb → react/thread_local_component_cache.rb} +0 -0
  11. data/lib/isomorfeus/top_level.rb +0 -7
  12. data/lib/isomorfeus_react/lucid_app/mixin.rb +8 -2
  13. data/lib/isomorfeus_react/lucid_app/native_component_constructor.rb +18 -7
  14. data/lib/isomorfeus_react/lucid_app/native_lucid_component_constructor.rb +1 -2
  15. data/lib/isomorfeus_react/lucid_component/api.rb +0 -27
  16. data/lib/isomorfeus_react/lucid_component/mixin.rb +8 -2
  17. data/lib/isomorfeus_react/lucid_component/native_component_constructor.rb +4 -4
  18. data/lib/isomorfeus_react/lucid_component/native_lucid_component_constructor.rb +4 -28
  19. data/lib/isomorfeus_react/lucid_component/styles_api.rb +34 -0
  20. data/lib/isomorfeus_react/lucid_func/mixin.rb +8 -2
  21. data/lib/isomorfeus_react/lucid_func/native_component_constructor.rb +3 -4
  22. data/lib/isomorfeus_react/react/function_component/api.rb +7 -6
  23. data/lib/isomorfeus_react/react/function_component/mixin.rb +5 -1
  24. data/lib/isomorfeus_react/react/function_component/native_component_constructor.rb +3 -2
  25. data/lib/isomorfeus_react/react/memo_component/mixin.rb +5 -1
  26. data/lib/isomorfeus_react/react/memo_component/native_component_constructor.rb +2 -3
  27. data/lib/isomorfeus_react_material/lucid_material/app/mixin.rb +1 -0
  28. data/lib/isomorfeus_react_material/lucid_material/app/native_component_constructor.rb +17 -6
  29. data/lib/isomorfeus_react_material/lucid_material/component/mixin.rb +1 -0
  30. data/lib/isomorfeus_react_material/lucid_material/component/native_component_constructor.rb +3 -3
  31. data/lib/isomorfeus_react_material/lucid_material/func/mixin.rb +1 -0
  32. data/lib/isomorfeus_react_material/lucid_material/func/native_component_constructor.rb +3 -4
  33. data/lib/isomorfeus_react_paper/lucid_paper/app/base.rb +9 -0
  34. data/lib/isomorfeus_react_paper/lucid_paper/app/mixin.rb +19 -0
  35. data/lib/isomorfeus_react_paper/lucid_paper/app/native_component_constructor.rb +32 -0
  36. data/lib/isomorfeus_react_paper/lucid_paper/component/base.rb +9 -0
  37. data/lib/isomorfeus_react_paper/lucid_paper/component/mixin.rb +18 -0
  38. data/lib/isomorfeus_react_paper/lucid_paper/component/native_component_constructor.rb +25 -0
  39. data/lib/isomorfeus_react_paper/lucid_paper/func/base.rb +9 -0
  40. data/lib/isomorfeus_react_paper/lucid_paper/func/mixin.rb +14 -0
  41. data/lib/isomorfeus_react_paper/lucid_paper/func/native_component_constructor.rb +71 -0
  42. data/lib/react.rb +159 -97
  43. data/lib/react/component/api.rb +5 -4
  44. data/lib/react/component/features.rb +1 -3
  45. data/lib/react/component/mixin.rb +5 -1
  46. data/lib/react/component/native_component_constructor.rb +4 -28
  47. data/lib/react/component/props.rb +4 -3
  48. data/lib/react/component/resolution.rb +47 -63
  49. data/lib/react/component/state.rb +4 -2
  50. data/lib/react/component/styles.rb +2 -23
  51. data/lib/react/context_wrapper.rb +3 -5
  52. data/lib/react/native_constant_wrapper.rb +0 -1
  53. data/lib/react/version.rb +1 -1
  54. data/lib/react_native/component/elements.rb +203 -0
  55. data/lib/react_native/lucid_app/react_native_component_constructor.rb +51 -0
  56. data/lib/react_native/lucid_component/react_native_component_constructor.rb +37 -0
  57. data/lib/react_native/lucid_func/react_native_component_constructor.rb +82 -0
  58. data/lib/react_native/react.rb +120 -0
  59. metadata +69 -23
  60. data/lib/isomorfeus/vivify_module.rb +0 -43
@@ -13,8 +13,10 @@ module React
13
13
  @native.JS.setState(new_state, `null`)
14
14
  end
15
15
  else
16
- return nil if `typeof #@native.state[key] === "undefined"`
17
- @native.JS[:state].JS[key]
16
+ %x{
17
+ if (typeof #@native.state[key] === 'undefined') { return nil; }
18
+ return #@native.state[key];
19
+ }
18
20
  end
19
21
  end
20
22
 
@@ -15,35 +15,14 @@ module React
15
15
  method_missing(prop, val)
16
16
  end
17
17
 
18
- %x{
19
- function isObject(obj) { return (obj && typeof obj === 'object'); }
20
-
21
- function mergeDeep(one, two) {
22
- return [one, two].reduce(function(pre, obj) {
23
- Object.keys(obj).forEach(function(key){
24
- let pVal = pre[key];
25
- let oVal = obj[key];
26
- if (Array.isArray(pVal) && Array.isArray(oVal)) {
27
- pre[key] = pVal.concat.apply(this, oVal);
28
- } else if (isObject(pVal) && isObject(oVal)) {
29
- pre[key] = mergeDeep(pVal, oVal);
30
- } else {
31
- pre[key] = oVal;
32
- }
33
- });
34
- return pre;
35
- }, {});
36
- }
37
- }
38
-
39
18
  def deep_merge(a_hash)
40
19
  native_hash = a_hash.to_n
41
- React::Component::Styles.new(`mergeDeep(#@native, native_hash)`)
20
+ React::Component::Styles.new(`Opal.React.merge_deep(#@native, native_hash)`)
42
21
  end
43
22
 
44
23
  def deep_merge!(a_hash)
45
24
  native_hash = a_hash.to_n
46
- `#@native = mergeDeep(#@native, native_hash)`
25
+ `#@native = Opal.React.merge_deep(#@native, native_hash)`
47
26
  self
48
27
  end
49
28
 
@@ -20,13 +20,11 @@ module React
20
20
  operabu.push([]);
21
21
  // console.log("consumer pushed", operabu, operabu.toString());
22
22
  let block_result = block.$call();
23
- if (block_result && (block_result.constructor === String || block_result.constructor === Number)) {
24
- operabu[operabu.length - 1].push(block_result);
25
- }
23
+ if (block_result && block_result !== nil) { Opal.React.render_block_result(block_result); }
26
24
  // console.log("consumer popping", operabu, operabu.toString());
27
25
  children = operabu.pop();
28
- if (children.length == 1) { children = children[0]; }
29
- else if (children.length == 0) { children = null; }
26
+ if (children.length === 1) { children = children[0]; }
27
+ else if (children.length === 0) { children = null; }
30
28
  }
31
29
  return children;
32
30
  });
@@ -8,7 +8,6 @@ module React
8
8
  end
9
9
 
10
10
  def method_missing(name, *args, &block)
11
- # language=JS
12
11
  %x{
13
12
  if (name[0] === 'u' && name[1] === 's' && name[2] === 'e') {
14
13
  if (name.indexOf('_') > 0) { name = Opal.React.lower_camelize(name); }
data/lib/react/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module React
2
- VERSION = '16.13.6'
2
+ VERSION = '16.13.11'
3
3
  end
@@ -0,0 +1,203 @@
1
+ module ReactNative
2
+ module Component
3
+ module Elements
4
+ # https://www.w3.org/TR/html52/fullindex.html#index-elements
5
+ # https://www.w3.org/TR/SVG11/eltindex.html
6
+ UNSUPPORTED_HTML_AND_SVG_ELEMENTS = %w[
7
+ a abbr address area article aside audio
8
+ base bdi bdo blockquote body br
9
+ canvas caption cite col colgroup
10
+ data datalist dd del details dfn dialog dl dt
11
+ em embed
12
+ fieldset figcaption figure footer form
13
+ head header hr html
14
+ iframe ins
15
+ kbd
16
+ label legend li link
17
+ main map mark meta meter
18
+ nav noscript
19
+ object ol optgroup option output
20
+ param picture progress
21
+ q
22
+ rp rt rtc ruby
23
+ s samp script section select small source span strong style sub summary sup
24
+ table tbody td template textarea tfoot th thead time title tr track
25
+ ul
26
+ var video
27
+ wbr
28
+
29
+ altGlyph altGlyphDef altGlyphItem animate animateColor animateMotion animateTransform
30
+ color-profile cursor
31
+ desc
32
+ feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting
33
+ feDisplacementMap feDistantLight feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur
34
+ feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting
35
+ feSpotLight feTile feTurbulence
36
+ filter font font-face font-face-format font-face-name font-face-src font-face-uri
37
+ glyph glyphRef
38
+ hkern
39
+ metadata missing-glyph mpath
40
+ script set style switch
41
+ tref
42
+ view vkern
43
+ ]
44
+
45
+ UNSUPPORTED_HTML_AND_SVG_ELEMENTS.each do |element|
46
+ define_method(element) do |*args, &block|
47
+ `console.warn("Element " + element + " is not yet supported, using a Text component as substitute!")`
48
+ `Opal.React.internal_prepare_args_and_render(Opal.global.Text, args, block)`
49
+ end
50
+ define_method(`element.toUpperCase()`) do |*args, &block|
51
+ `console.warn("Element " + element + " is not yet supported, using a Text component as substitute!")`
52
+ `Opal.React.internal_prepare_args_and_render(Opal.global.Text, args, block)`
53
+ end
54
+ end
55
+
56
+ # button
57
+ %x{
58
+ self['supported_button'] = function(props) {
59
+ let theme = Opal.global.React.useContext(Opal.global.ThemeContext);
60
+ let style = {};
61
+ if (theme && typeof theme['button'] !== 'undefined') { style = theme['button']; }
62
+ if (typeof props.style !== 'undefined') {
63
+ style = Opal.React.merge_deep(style, props.style);
64
+ }
65
+ let new_props = Object.assign({}, props, { style: style });
66
+ if (typeof props.title === 'undefined') {
67
+ try {
68
+ new_props.title = props.children.props.children;
69
+ } catch (e) {
70
+ console.error("BUTTON accepts only one string child!")
71
+ }
72
+ }
73
+ return Opal.global.React.createElement(Opal.global.Button, new_props);
74
+ }
75
+ self['supported_button'].displayName = 'BUTTON';
76
+ }
77
+ define_method('button') do |*args, &block|
78
+ `Opal.React.internal_prepare_args_and_render(Opal.ReactNative.Component.Elements.supported_button, args, block)`
79
+ end
80
+ define_method('BUTTON') do |*args, &block|
81
+ `Opal.React.internal_prepare_args_and_render(Opal.ReactNative.Component.Elements.supported_button, args, block)`
82
+ end
83
+
84
+ # img
85
+ define_method('img') do |*args, &block|
86
+ `Opal.React.internal_prepare_args_and_render(Opal.global.Image, args, block)`
87
+ end
88
+ define_method('IMG') do |*args, &block|
89
+ `Opal.React.internal_prepare_args_and_render(Opal.global.Image, args, block)`
90
+ end
91
+
92
+ # input
93
+ %x{
94
+ self.supported_input = function(props) {
95
+ Opal.React.render_buffer.push([]);
96
+ if (typeof props.type !== 'undefined') {
97
+ if (props.type === 'text') { return Opal.React.internal_prepare_args_and_render(Opal.global.TextInput, props); }
98
+ else if (props.type === 'checkbox') { return Opal.React.internal_prepare_args_and_render(Opal.global.InputSwitch, props); }
99
+ else {
100
+ console.warn("Input type " + props.type + " not supported. Using TextInput as substitute!");
101
+ return Opal.React.internal_prepare_args_and_render(Opal.global.TextInput, props);
102
+ }
103
+ }
104
+ Opal.React.internal_prepare_args_and_render(Opal.global.TextInput, props);
105
+ return Opal.React.render_buffer.pop();
106
+ }
107
+ }
108
+ define_method('input') do |*args, &block|
109
+ `Opal.React.internal_prepare_args_and_render(Opal.ReactNative.Component.Elements.supported_input, args, block)`
110
+ end
111
+ define_method('INPUT') do |*args, &block|
112
+ `Opal.React.internal_prepare_args_and_render(Opal.ReactNative.Component.Elements.supported_input, args, block)`
113
+ end
114
+
115
+ # elements that map to Text with style
116
+ SUPPORTED_TEXT_HTML_ELEMENTS = %w[
117
+ b
118
+ code
119
+ h1 h2 h3 h4 h5 h6
120
+ i
121
+ pre
122
+ span
123
+ u
124
+ ]
125
+
126
+ SUPPORTED_TEXT_HTML_ELEMENTS.each do |element|
127
+ fun_name = 'supported_' + element
128
+ %x{
129
+ self[fun_name] = function(props) {
130
+ let theme = Opal.global.React.useContext(Opal.global.ThemeContext);
131
+ let style = {};
132
+ if (theme && typeof theme[element] !== 'undefined') { style = theme[element]; }
133
+ if (typeof props.style !== 'undefined') {
134
+ style = Opal.React.merge_deep(style, props.style);
135
+ }
136
+ let new_props = Object.assign({}, props, { style: style });
137
+ return Opal.global.React.createElement(Opal.global.Text, new_props);
138
+ }
139
+ self[fun_name].displayName = element.toUpperCase();
140
+ }
141
+ define_method(`element.toLowerCase()`) do |*args, &block|
142
+ `Opal.React.internal_prepare_args_and_render(Opal.ReactNative.Component.Elements[fun_name], args, block)`
143
+ end
144
+ define_method(`element.toUpperCase()`) do |*args, &block|
145
+ `Opal.React.internal_prepare_args_and_render(Opal.ReactNative.Component.Elements[fun_name], args, block)`
146
+ end
147
+ end
148
+
149
+ SUPPORTED_VIEW_HTML_ELEMENTS = %w[
150
+ div
151
+ p
152
+ ]
153
+
154
+ SUPPORTED_VIEW_HTML_ELEMENTS.each do |element|
155
+ fun_name = 'supported_' + element
156
+ %x{
157
+ self[fun_name] = function(props) {
158
+ let theme = Opal.global.React.useContext(Opal.global.ThemeContext);
159
+ let style = {};
160
+ if (theme && typeof theme[element] !== 'undefined') { style = theme[element]; }
161
+ if (typeof props.style !== 'undefined') {
162
+ style = Opal.React.merge_deep(style, props.style);
163
+ }
164
+ let new_props = Object.assign({}, props, { style: style });
165
+ return Opal.global.React.createElement(Opal.global.View, new_props);
166
+ }
167
+ self[fun_name].displayName = element.toUpperCase();
168
+ }
169
+ define_method(`element.toLowerCase()`) do |*args, &block|
170
+ `Opal.React.internal_prepare_args_and_render(Opal.ReactNative.Component.Elements[fun_name], args, block)`
171
+ end
172
+ define_method(`element.toUpperCase()`) do |*args, &block|
173
+ `Opal.React.internal_prepare_args_and_render(Opal.ReactNative.Component.Elements[fun_name], args, block)`
174
+ end
175
+ end
176
+
177
+ SUPPORTED_SVG_ELEMENTS = %w[
178
+ Circle ClipPath
179
+ Defs
180
+ Ellipse
181
+ ForeignObject
182
+ G
183
+ Image
184
+ Line LinearGradient
185
+ Marker Mask
186
+ Path Pattern Polygon Polyline
187
+ RadialGradient Rect
188
+ Stop Svg Symbol
189
+ Text TextPath TSpan
190
+ Use
191
+ ]
192
+
193
+ SUPPORTED_SVG_ELEMENTS.each do |element|
194
+ define_method(`element.toLowerCase()`) do |*args, &block|
195
+ `Opal.React.internal_prepare_args_and_render(Opal.global.ReactNativeSvg[element], args, block)`
196
+ end
197
+ define_method(`element.toUpperCase()`) do |*args, &block|
198
+ `Opal.React.internal_prepare_args_and_render(Opal.global.ReactNativeSvg[element], args, block)`
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,51 @@
1
+ module LucidApp
2
+ module ReactNativeComponentConstructor
3
+ # for should_component_update we apply ruby semantics for comparing props
4
+ # to do so, we convert the props to ruby hashes and then compare
5
+ # this makes sure, that for example rubys Nil object gets handled properly
6
+ def self.extended(base)
7
+ component_name = base.to_s + 'Wrapper'
8
+ theme_component_name = base.to_s + 'ThemeWrapper'
9
+ # language=JS
10
+ %x{
11
+ base.jss_theme = {};
12
+ base.themed_react_component = function(props) {
13
+ const opag = Opal.global;
14
+ const theme = opag.React.useContext(opag.ThemeContext);
15
+ let classes;
16
+ if (base.jss_styles) {
17
+ if (!base.use_styles || (Opal.Isomorfeus.development === true)) {
18
+ let styles;
19
+ if (typeof base.jss_styles === 'function') { styles = base.jss_styles(theme); }
20
+ else { styles = base.jss_styles; }
21
+ base.use_styles = Opal.React.merge_deep(theme, opag.StyleSheet.create(styles));
22
+ }
23
+ classes = base.use_styles;
24
+ } else {
25
+ classes = theme;
26
+ }
27
+ let themed_classes_props = Object.assign({}, props, { classes: classes, theme: theme });
28
+ return opag.React.createElement(base.lucid_react_component, themed_classes_props);
29
+ };
30
+ base.themed_react_component.displayName = #{theme_component_name};
31
+ base.react_component = class extends Opal.global.React.Component {
32
+ constructor(props) {
33
+ super(props);
34
+ if (Opal.Isomorfeus.$top_component() == nil) { Opal.Isomorfeus['$top_component='](this); }
35
+ }
36
+ static get displayName() {
37
+ return "IsomorfeusTopLevelComponent";
38
+ }
39
+ static set displayName(new_name) {
40
+ // dont do anything here except returning the set value
41
+ return new_name;
42
+ }
43
+ render() {
44
+ let themed_component = Opal.global.React.createElement(base.themed_react_component, this.props);
45
+ return Opal.global.React.createElement(Opal.global.ThemeContext.Provider, { value: Opal.global.DefaultTheme }, themed_component);
46
+ }
47
+ }
48
+ }
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ module LucidComponent
2
+ module ReactNativeComponentConstructor
3
+ # for should_component_update we apply ruby semantics for comparing props
4
+ # to do so, we convert the props to ruby hashes and then compare
5
+ # this makes sure, that for example rubys Nil object gets handled properly
6
+ def self.extended(base)
7
+ component_name = base.to_s + 'Wrapper'
8
+ # language=JS
9
+ %x{
10
+ base.react_component = Opal.global.React.memo(function(props) {
11
+ let opag = Opal.global;
12
+ let store;
13
+ if (base.store_updates) { store = opag.React.useContext(opag.LucidApplicationContext); }
14
+ const theme = opag.React.useContext(opag.ThemeContext);
15
+ let classes;
16
+ if (base.jss_styles) {
17
+ if (!base.use_styles || (Opal.Isomorfeus.development === true)) {
18
+ let styles;
19
+ if (typeof base.jss_styles === 'function') { styles = base.jss_styles(theme); }
20
+ else { styles = base.jss_styles; }
21
+ base.use_styles = Opal.React.merge_deep(theme, opag.StyleSheet.create(styles));
22
+ }
23
+ classes = base.use_styles;
24
+ } else {
25
+ classes = theme;
26
+ }
27
+ let new_props = Object.assign({}, props);
28
+ new_props.theme = theme;
29
+ new_props.classes = classes;
30
+ new_props.store = store;
31
+ return opag.React.createElement(base.lucid_react_component, new_props);
32
+ }, Opal.React.props_are_equal);
33
+ base.react_component.displayName = #{component_name};
34
+ }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,82 @@
1
+ module LucidFunc
2
+ module ReactNativeComponentConstructor
3
+ def self.extended(base)
4
+ component_name = base.to_s
5
+ %x{
6
+ base.store_updates = true;
7
+ base.equality_checker = null;
8
+ base.instance_init = function(initial) {
9
+ let ruby_state = { instance: #{base.new(`{}`)} };
10
+ ruby_state.instance.__ruby_instance = ruby_state.instance;
11
+ ruby_state.instance.data_access = function() { return this.props.store; }
12
+ ruby_state.instance.data_access.bind(ruby_state.instance);
13
+ return ruby_state;
14
+ }
15
+ base.instance_reducer = function(state, action) { return state; }
16
+ base.react_component = Opal.global.React.memo(function(props) {
17
+ const og = Opal.global;
18
+ const oper = Opal.React;
19
+ oper.render_buffer.push([]);
20
+ // console.log("function pushed", oper.render_buffer, oper.render_buffer.toString());
21
+ // Lucid functionality
22
+ let classes = null;
23
+ let store;
24
+ if (base.store_updates) { store = og.React.useContext(og.LucidApplicationContext); }
25
+ let theme = og.ReactJSS.useTheme();
26
+ if (base.jss_styles) {
27
+ if (!base.use_styles || (Opal.Isomorfeus.development === true)) {
28
+ let styles;
29
+ if (typeof base.jss_styles === 'function') { styles = base.jss_styles(theme); }
30
+ else { styles = base.jss_styles; }
31
+ base.use_styles = og.ReactJSS.createUseStyles(styles);
32
+ }
33
+ classes = base.use_styles();
34
+ }
35
+ // prepare Ruby instance
36
+ const [__ruby_state, __ruby_dispatch] = og.React.useReducer(base.instance_reducer, null, base.instance_init);
37
+ const __ruby_instance = __ruby_state.instance;
38
+ __ruby_instance.props = Object.assign({}, props);
39
+ __ruby_instance.props.store = store;
40
+ __ruby_instance.props.theme = theme;
41
+ __ruby_instance.props.classes = classes;
42
+ oper.active_components.push(__ruby_instance);
43
+ oper.active_redux_components.push(__ruby_instance);
44
+ let block_result = #{`__ruby_instance`.instance_exec(&`base.render_block`)};
45
+ if (block_result && block_result !== nil) { oper.render_block_result(block_result); }
46
+ oper.active_redux_components.pop();
47
+ oper.active_components.pop();
48
+ // console.log("function popping", oper.render_buffer, oper.render_buffer.toString());
49
+ let result = oper.render_buffer.pop();
50
+ if (result.length === 1) { return result[0]; }
51
+ return result;
52
+ }, base.equality_checker);
53
+ base.react_component.displayName = #{component_name};
54
+ }
55
+
56
+ base_module = base.to_s.deconstantize
57
+ if base_module != ''
58
+ base_module.constantize.define_singleton_method(base.to_s.demodulize) do |*args, &block|
59
+ `Opal.React.internal_prepare_args_and_render(#{base}.react_component, args, block)`
60
+ end
61
+ else
62
+ Object.define_method(base.to_s) do |*args, &block|
63
+ `Opal.React.internal_prepare_args_and_render(#{base}.react_component, args, block)`
64
+ end
65
+ end
66
+
67
+ def props_are_equal?(&block)
68
+ %x{
69
+ base.equality_checker = function (prev_props, next_props) {
70
+ var prev_ruby_props = Opal.React.Component.Props.$new({props: prev_props});
71
+ var next_ruby_props = Opal.React.Component.Props.$new({props: next_props});
72
+ return #{block.call(`prev_ruby_props`, `next_ruby_props`)};
73
+ }
74
+ }
75
+ end
76
+
77
+ def render(&block)
78
+ `base.render_block = #{block}`
79
+ end
80
+ end
81
+ end
82
+ end