class UComponent def self.defaults {} end def self.build(_doc, _options) raise NotImplementedError end def self.call(*args) fn = args[0].is_a?(Proc) ? args[0] : method(:build) options = args[0].is_a?(Hash) ? args[0] : (args[1] || {}) new.call(options, fn) end def self.to_proc -> (*args) { call(*args) } end def initialize(fn = nil) @fn = fn end def call(opt = {}, fn = nil) func = @fn || fn options = opt.is_a?(Hash) ? self.class.defaults.merge(opt) : opt builder = Nokogiri::HTML::Builder.new do |doc| func.arity == 1 ? func.(doc) : func.(doc, options) end builder.to_html.sub!(/]/, '') end def to_proc -> (options) { call(options) } end end class HelloWorld < UComponent def self.build(doc) doc.span.bold { doc.text("Hello world") } end end class HelloWorld2 < UComponent def self.build(doc, message:) doc.span.bold { doc.text(message) } end end class Greet < UComponent def self.defaults { name: 'John Doe' } end def self.build(doc, name:) doc.span.bar.foo! { doc.text("Hello #{name}") } end end # == Examples == HelloWorld.call #=> "Hello world\n" HelloWorld2.call message: 'Olá mundo' #=> "Olá mundo\n" UComponent.call -> doc { doc.span.bold { doc.text("Hello world") } } #=> "Hello world\n" UComponent.call( -> doc, message { doc.span.bold { doc.text(message) } }, 'Olá mundo' ) #=> "Olá mundo\n" Greet.call #=> "Hello John Doe\n" Greet.call name: 'Serradura' #=> "Hello Serradura\n" [{name: 'Rodrigo'}, {name: 'Talita'}, {}].map(&Greet) #=> ["Hello Rodrigo\n", "Hello Talita\n", "Hello John Doe\n"] greet = UComponent.new(-> (doc, name:) do doc.span.bar.foo! { doc.text("Hello #{name}") } end) greet.call(name: 'Bella') #=> "Hello Bella\n" [{name: 'Rodrigo'}, {name: 'Talita'}].map(&greet) #=> ["Hello Rodrigo\n", "Hello Talita\n"] number = UComponent.new(-> (doc, n) do doc.strong { doc.text(n) } end) [1,2,3,4].map(&number) #=> ["1\n", "2\n", "3\n", "4\n"]