class Ovto::Component
Public Class Methods
hash_to_js_obj(hash)
click to toggle source
# File lib/ovto/component.rb, line 8 def self.hash_to_js_obj(hash) ret = `{}` hash.each do |k, v| `ret[k] = v` end ret end
middleware_name()
click to toggle source
(internal) Defined for convenience
# File lib/ovto/component.rb, line 17 def self.middleware_name WiredActionSet::I_AM_APP_NOT_A_MIDDLEWARE end
new(wired_action_set, middleware_path=[])
click to toggle source
# File lib/ovto/component.rb, line 21 def initialize(wired_action_set, middleware_path=[]) @wired_action_set = wired_action_set || WiredActionSet.dummy @middleware_path = middleware_path # Initialize here for the unit tests @vdom_stack = [[]] @components = [] @components_index = 0 end
Public Instance Methods
render()
click to toggle source
# File lib/ovto/component.rb, line 30 def render '' end
state()
click to toggle source
# File lib/ovto/component.rb, line 34 def state @wired_action_set.app.state end
Private Instance Methods
actions()
click to toggle source
# File lib/ovto/component.rb, line 71 def actions return @middleware_path.inject(@wired_action_set){|wa_set, middleware_name| wa_set[middleware_name] }[WiredActionSet::THE_MIDDLEWARE_ITSELF] end
do_render(args, state, &block)
click to toggle source
Call render
to generate VDom
# File lib/ovto/component.rb, line 47 def do_render(args, state, &block) Ovto.debug_trace_log("rendering #{self}") @vdom_stack = [[]] @components_index = 0 @done_render = false @current_state = state rendered = Ovto.log_error { Ovto.send_args_with_state(self, :render, args, state, &block) } case rendered when String return rendered when Array if rendered.length > 1 raise MoreThanOneNode end raise "rendered is empty" if rendered.length == 0 return rendered[0] else console.error("#render returned unknown value", rendered) raise "#render returned unknown value" end end
extract_attrs(tag_name)
click to toggle source
# File lib/ovto/component.rb, line 128 def extract_attrs(tag_name) case tag_name when /^([^.#]*)\.([-\w]+)(\#([-\w]+))?/ # a.b#c tag_name, class_name, id = ($1.empty? ? 'div' : $1), $2, $4 when /^([^.#]*)\#([-\w]+)(\.([-\w]+))?/ # a#b.c tag_name, class_name, id = ($1.empty? ? 'div' : $1), $4, $2 else class_name = id = nil end attributes = {} attributes[:class] = class_name if class_name attributes[:id] = id if id return tag_name, attributes end
merge_attrs(base_attributes, attributes)
click to toggle source
Merge attributes into base_attributes, with special care for `class:`
# File lib/ovto/component.rb, line 144 def merge_attrs(base_attributes, attributes) base_class = base_attributes[:class] more_class = attributes[:class] merged_class = if base_class && more_class base_class + " " + more_class else base_class || more_class end if merged_class base_attributes.merge(attributes).merge(:class => merged_class) else base_attributes.merge(attributes) end end
new_component(comp_class)
click to toggle source
# File lib/ovto/component.rb, line 218 def new_component(comp_class) comp = @components[@components_index] if comp.is_a?(comp_class) @components_index += 1 return comp end middleware_path = new_middleware_path(comp_class) comp = @components[@components_index] = comp_class.new(@wired_action_set, middleware_path) @components_index += 1 comp end
new_middleware_path(comp_class)
click to toggle source
Make new middleware_path by adding comp_class
# File lib/ovto/component.rb, line 232 def new_middleware_path(comp_class) mw_name = comp_class.middleware_name if (idx = @middleware_path.index(mw_name)) # eg. suppose OvtoIde uses OvtoWindow # class CompI < OvtoIde::Component # def render # o Window do # o AnotherComponentOfOvtoIde # end # end # end # class Window < OvtoWindow::Component # def render(&block) # o ".window", &block # end # end # Rendering order: # 1. CompI (ovto_ide) # 2. Window (ovto_window) # 3. AnotherComponentOfOvtoIde (ovto_ide again) @middleware_path[0..idx] else if comp_class.middleware_name == WiredActionSet::I_AM_APP_NOT_A_MIDDLEWARE @middleware_path else @middleware_path + [comp_class.middleware_name] end end # TODO: it would be nice if we could raise an error when comp_class # is invalid middleware (i.e. not use'd) end
o(_tag_name, arg1=nil, arg2=nil, &block)
click to toggle source
o 'div', 'Hello.' o 'div', class: 'main', 'Hello.' o 'div', style: {color: 'red'}, 'Hello.' o 'div.main' o 'div#main' o 'div' do 'Hello.' end o 'div' do
o 'h1', 'Hello.'
end o 'div', `{nodeName: .…}` # Inject VDom spec directly o SubComponentClass o SubComponentClass do … end # Ovto
passes the block to SubComponent#render
# File lib/ovto/component.rb, line 89 def o(_tag_name, arg1=nil, arg2=nil, &block) if native?(arg1) # Embed VDom directly attributes = {} content = arg1 elsif arg1.is_a?(Hash) # Has attributes attributes = arg1 content = arg2 elsif arg2 == nil # Has content instead of attributes, or both are nil attributes = {} content = arg1 else raise ArgumentError end case _tag_name when Class if content raise ArgumentError, "use a block to pass content to sub component" end result = render_component(_tag_name, attributes, &block) when 'text' unless attributes.empty? raise ArgumentError, "text cannot take attributes" end result = content when String children = render_children(content, block) tag_name, base_attributes = *extract_attrs(_tag_name) # Ignore nil/false more_attributes = attributes.reject{|k, v| !v} result = render_tag(tag_name, merge_attrs(base_attributes, more_attributes), children) else raise TypeError, "tag_name must be a String or Component but got "+ Ovto.inspect(tag_name) end @vdom_stack.last.push(result) return @vdom_stack.last end
render_block(block)
click to toggle source
# File lib/ovto/component.rb, line 176 def render_block(block) @vdom_stack.push [] orig_depth = @vdom_stack.length block_value = block.call @vdom_stack = @vdom_stack.first(orig_depth) results = @vdom_stack.pop if results.length > 0 # 'o' was called at least once results elsif native?(block_value) # Inject VDom tree written in JS object # eg. Embed markdown [block_value] elsif block_value.is_a?(String) # When 'o' is never called in the child block, use the last value # eg. # o 'span' do # 'Hello' #=> This will be the content of the span tag # end [block_value] elsif block_value.is_a?(Array) # Case 1 # o "div", &block # Case 2 # items = [] # o 'div' do items.each{ o ... } end # == o 'div' do [] end block_value else console.error("Invalid block_value:", Ovto.inspect(block_value)) raise "Invalid block value" end end
render_children(content=nil, block=nil)
click to toggle source
# File lib/ovto/component.rb, line 159 def render_children(content=nil, block=nil) case when content && block raise ArgumentError, "o cannot take both content and block" when content if native?(content) [content] else [content.to_s] end when block render_block(block) else [] end end
render_component(comp_class, args, &block)
click to toggle source
Instantiate component and call its render
to get VDom
# File lib/ovto/component.rb, line 210 def render_component(comp_class, args, &block) comp = new_component(comp_class) orig_stack, @vdom_stack = @vdom_stack, [[]] ret = comp.do_render(args, @current_state, &block) @vdom_stack = orig_stack ret end
render_tag(tag_name, attributes, children)
click to toggle source
# File lib/ovto/component.rb, line 264 def render_tag(tag_name, attributes, children) attributes_ = attributes.map{|k, v| if k.start_with?("on") # Inject log_error to event handlers [k, ->(e){ Ovto.log_error{ v.call(e) }}] else [k, v] end }.to_h js_attributes = Component.hash_to_js_obj(attributes_ || {}) if (style = attributes_['style']) `js_attributes.style = #{Component.hash_to_js_obj(style)}` end children ||= `null` ret = %x{ { nodeName: tag_name, attributes: js_attributes, children: children, key: js_attributes.key } } ret end
render_view(state)
click to toggle source
Render entire MyApp::MainComponent Called from runtime.rb
# File lib/ovto/component.rb, line 42 def render_view(state) do_render({}, state) end