Skip to content

Instantly share code, notes, and snippets.

@danieldocki
Forked from serradura/u-ui.rb
Created April 28, 2022 12:09
Show Gist options
  • Select an option

  • Save danieldocki/ca81f8f67cdf9a30a2bd7f38111378d6 to your computer and use it in GitHub Desktop.

Select an option

Save danieldocki/ca81f8f67cdf9a30a2bd7f38111378d6 to your computer and use it in GitHub Desktop.

Revisions

  1. @serradura serradura revised this gist Feb 11, 2019. 1 changed file with 9 additions and 3 deletions.
    12 changes: 9 additions & 3 deletions u-ui.rb
    Original file line number Diff line number Diff line change
    @@ -190,7 +190,7 @@ def test_component_composition
    # -- Feature: global aliases --

    class UI::Button < UI::Component
    name :button, global: true
    set name: :button, global: true

    template { button { data } }
    end
    @@ -212,7 +212,7 @@ class UI::Button2 < UI::Component
    end

    class UI::Button4 < UI::Component
    name :button4
    set name: :button4

    template { button { data } }
    end
    @@ -236,11 +236,17 @@ class UI::Button4 < UI::Component
    class UI::Button5 < UI::Component
    namespace :foo

    name :button5
    set name: :button5

    template { button { data } }
    end

    # class UI::Button5 < UI::Component
    # set name: :button5, namespace: :foo

    # template { button { data } }
    # end

    UI.button5 # => raises NoMethodError

    UI[:foo].button5.call('My button') # => "<button>My Button</button>\n"
  2. @serradura serradura revised this gist Feb 11, 2019. 1 changed file with 15 additions and 20 deletions.
    35 changes: 15 additions & 20 deletions u-ui.rb
    Original file line number Diff line number Diff line change
    @@ -79,9 +79,10 @@ def build_html_with(data)
    end

    def apply_defaults_to(data)
    return @defaults.merge(data) if [data, @defaults].all?(Hash)
    return data || @defaults unless data.is_a?(Hash)
    return @defaults.merge(data) if @defaults.is_a?(Hash)

    data || @defaults
    data.empty? ? @defaults : data
    end
    end
    end
    @@ -104,9 +105,9 @@ class HelloWorld < UI::Component
    end

    def test_hello_world_component
    expected = "<span class=\"bold\">Hello world</span>\n"
    expected = "<span class=\"bar\" id=\"foo\">Hello World</span>\n"

    assert expected, HelloWorld.call
    assert expected == HelloWorld.call
    end

    class Greet < UI::Component
    @@ -122,15 +123,15 @@ class Greet < UI::Component
    def test_greet_component
    expected_default_name = "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"

    assert expected_default_name, Greet.call
    assert(expected_default_name == Greet.call)

    expected_rodrigo_name = "<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n"

    assert expected_rodrigo_name, Greet.call(name: 'Rodrigo')
    assert(expected_rodrigo_name == Greet.call(name: 'Rodrigo'))

    expected_collection = [expected_default_name, expected_rodrigo_name]

    assert expected_collection, [{}, {name: 'Rodrigo'}].map(&Greet)
    assert(expected_collection == [{}, {name: 'Rodrigo'}].map(&Greet))
    end

    class Number < UI::Component
    @@ -144,33 +145,27 @@ class Number < UI::Component
    def test_number_component
    expected_number_zero = "<strong>0</strong>\n"

    assert expected_number_zero, Number.call
    assert(expected_number_zero == Number.call)

    expected_number_one = "<strong>1</strong>\n"

    assert expected_number_one, Number.call(1)
    assert(expected_number_one == Number.call(1))

    expected_collection = [expected_number_zero, expected_number_one]

    assert expected_collection, [nil, 1].map(&Number)
    assert(expected_collection == [nil, 1].map(&Number))
    end

    def test_fn_component
    number = UI::Component.fn do
    strong { text(data) }
    end

    expected_number_zero = "<strong>0</strong>\n"

    assert expected_number_zero, number.call

    expected_number_one = "<strong>1</strong>\n"

    assert expected_number_one, number.call(1)

    expected_collection = [expected_number_zero, expected_number_one]
    assert(expected_number_one == number.call(1))

    assert expected_collection, [nil, 1].map(&number)
    assert([expected_number_one] == [1].map(&number))
    end

    def test_component_composition
    @@ -180,9 +175,9 @@ def test_component_composition
    strong { text(content) }
    end

    assert "<div><strong>1</strong>\n</div>\n", container.call(number.(content: 1))
    assert("<div><strong>1</strong>\n</div>\n" == container.call(number.(content: 1)))

    assert ["<div><strong>1</strong>\n</div>\n"], [content: 1].map(&number).map(&container)
    assert(["<div><strong>1</strong>\n</div>\n"] == [content: 1].map(&number).map(&container))
    end
    end

  3. @serradura serradura revised this gist Feb 11, 2019. 1 changed file with 10 additions and 0 deletions.
    10 changes: 10 additions & 0 deletions u-ui.rb
    Original file line number Diff line number Diff line change
    @@ -216,15 +216,25 @@ class UI::Button2 < UI::Component
    template { button.btn3! { data } }
    end

    class UI::Button4 < UI::Component
    name :button4

    template { button { data } }
    end

    UI::Components.add UI::Button4

    UI.button.call('My button') # => "<button>My Button</button>\n"
    UI.button1.call('My button') # => "<button>My Button</button>\n"
    UI.button2.call('My button') # => "<button class=\"btn2\">My Button</button>\n"
    UI.button3.call('My button') # => "<button id=\"btn3\">My Button</button>\n"
    UI.button4.call('My button') # => "<button>My Button</button>\n"

    UI.button { 'My button' } # => "<button>My Button</button>\n"
    UI.button1 { 'My button' } # => "<button>My Button</button>\n"
    UI.button2 { 'My button' } # => "<button class=\"btn2\">My Button</button>\n"
    UI.button3 { 'My button' } # => "<button id=\"btn3\">My Button</button>\n"
    UI.button4 { 'My button' } # => "<button>My Button</button>\n"

    # -- Feature: Namespace --

  4. @serradura serradura revised this gist Feb 11, 2019. 1 changed file with 97 additions and 5 deletions.
    102 changes: 97 additions & 5 deletions u-ui.rb
    Original file line number Diff line number Diff line change
    @@ -21,6 +21,10 @@ def data=(value)
    end

    def data; @data; end

    def content
    @data[:content] || @data['content'.freeze] if @data.respond_to?(:[])
    end
    end
    end

    @@ -173,17 +177,105 @@ def test_component_composition
    container = UI.component { div data }

    number = UI.component do
    strong { text(data) }
    strong { text(content) }
    end

    assert "<div><strong>0</strong>\n</div>\n", container.call(number.call)
    assert "<div><strong>1</strong>\n</div>\n", container.call(number.(content: 1))

    assert "<div><strong>1</strong>\n</div>\n", container.call(number.(1))

    assert ["<div><strong>1</strong>\n</div>\n"], [1].map(&number).map(&container)
    assert ["<div><strong>1</strong>\n</div>\n"], [content: 1].map(&number).map(&container)
    end
    end

    # == test runner ==

    Microtest.call

    # == ideas ==

    # -- Feature: global aliases --

    class UI::Button < UI::Component
    name :button, global: true

    template { button { data } }
    end

    class UI::Button1 < UI::Component
    global_name :button

    template { button { data } }
    end

    class UI::Button2 < UI::Component
    template { button.btn2 { data } }
    end

    UI::Components.add :button2, UI::Button2

    UI::Components.add :button3 do
    template { button.btn3! { data } }
    end

    UI.button.call('My button') # => "<button>My Button</button>\n"
    UI.button1.call('My button') # => "<button>My Button</button>\n"
    UI.button2.call('My button') # => "<button class=\"btn2\">My Button</button>\n"
    UI.button3.call('My button') # => "<button id=\"btn3\">My Button</button>\n"

    UI.button { 'My button' } # => "<button>My Button</button>\n"
    UI.button1 { 'My button' } # => "<button>My Button</button>\n"
    UI.button2 { 'My button' } # => "<button class=\"btn2\">My Button</button>\n"
    UI.button3 { 'My button' } # => "<button id=\"btn3\">My Button</button>\n"

    # -- Feature: Namespace --

    class UI::Button5 < UI::Component
    namespace :foo

    name :button5

    template { button { data } }
    end

    UI.button5 # => raises NoMethodError

    UI[:foo].button5.call('My button') # => "<button>My Button</button>\n"
    UI[:foo].button5 { 'My button' } # => "<button>My Button</button>\n"

    foo_components = UI[:foo]
    foo_components.button5 # => "<button>My Button</button>\n"

    # -- Feature: SETUP --

    class UI::Hello1 < UI::Component
    template { span data[:name] }

    setup do |data|
    data[:name] = upcase(data[:name])
    end

    def upcase(value)
    String(value).upcase
    end
    end

    hello1 = UI::Hello1.new
    hello1.call name: :foo # => "<span>FOO</span>\n"
    hello1.upcase :foo # => 'FOO'

    class UI::Hello2 < UI::Component
    template { span data[:name] }

    def setup(data)
    data[:name] = upcase(data[:name])
    end

    private

    def upcase(value)
    String(value).upcase
    end
    end

    hello2 = UI::Hello2.new
    hello2.call name: :foo # => "<span>FOO</span>\n"
    hello2.upcase :foo # NoMethodError (private method)
  5. @serradura serradura revised this gist Feb 10, 2019. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion u-ui.rb
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@

    # frozen_string_literal: true

    # == Gemfile ==
  6. @serradura serradura revised this gist Feb 10, 2019. 1 changed file with 8 additions and 2 deletions.
    10 changes: 8 additions & 2 deletions u-ui.rb
    Original file line number Diff line number Diff line change
    @@ -83,6 +83,12 @@ def apply_defaults_to(data)
    end
    end

    module UI
    def self.component(&block)
    UI::Component.fn(&block)
    end
    end

    # == test ==

    class UITest < Microtest::Test
    @@ -165,9 +171,9 @@ def test_fn_component
    end

    def test_component_composition
    container = UI::Component.fn { div data }
    container = UI.component { div data }

    number = UI::Component.fn do
    number = UI.component do
    strong { text(data) }
    end

  7. @serradura serradura revised this gist Feb 10, 2019. 3 changed files with 184 additions and 221 deletions.
    184 changes: 184 additions & 0 deletions u-ui.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,184 @@

    # frozen_string_literal: true

    # == Gemfile ==

    require 'bundler/inline'

    gemfile do
    source 'https://rubygems.org'

    gem 'u-test', '0.9.0'

    gem 'nokogiri', '>= 1.5'
    end

    # == lib ==

    module UI
    module DataAccessor
    def data=(value)
    @data = value
    end

    def data; @data; end
    end
    end

    module UI
    require 'cgi'
    require 'nokogiri/html'

    class Component
    def self.fn(&block)
    new(block)
    end

    def self.call(data = {})
    new.call(data)
    end

    def self.to_proc
    -> (data = {}) { call(data) }
    end

    def self.template(&block)
    define_method(:__template) { block }
    end

    def self.defaults(data = {})
    define_method(:__defaults) { data }
    end

    def initialize(fn = nil)
    @fn = fn || __template

    @defaults = respond_to?(:__defaults) ? __defaults : {}
    end

    def call(data = nil)
    CGI.unescapeHTML(build_html_with(data))
    end

    def to_proc
    -> (data) { call(data) }
    end

    private

    def build_html_with(data)
    builder = Nokogiri::HTML::Builder.new
    builder.context = builder
    builder.extend(DataAccessor)
    builder.data = apply_defaults_to(data)
    builder.instance_eval(&@fn)
    builder.to_html.sub!(/<!DOCTYPE.*[^>]/, '')
    end

    def apply_defaults_to(data)
    return @defaults.merge(data) if [data, @defaults].all?(Hash)

    data || @defaults
    end
    end
    end

    # == test ==

    class UITest < Microtest::Test
    class HelloWorld < UI::Component
    template do
    span.bar.foo! {
    text("Hello World")
    }
    end
    end

    def test_hello_world_component
    expected = "<span class=\"bold\">Hello world</span>\n"

    assert expected, HelloWorld.call
    end

    class Greet < UI::Component
    defaults name: 'John Doe'

    template do
    span.bar.foo! {
    text("Hello #{data[:name]}")
    }
    end
    end

    def test_greet_component
    expected_default_name = "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"

    assert expected_default_name, Greet.call

    expected_rodrigo_name = "<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n"

    assert expected_rodrigo_name, Greet.call(name: 'Rodrigo')

    expected_collection = [expected_default_name, expected_rodrigo_name]

    assert expected_collection, [{}, {name: 'Rodrigo'}].map(&Greet)
    end

    class Number < UI::Component
    defaults 0

    template do
    strong { text(data) }
    end
    end

    def test_number_component
    expected_number_zero = "<strong>0</strong>\n"

    assert expected_number_zero, Number.call

    expected_number_one = "<strong>1</strong>\n"

    assert expected_number_one, Number.call(1)

    expected_collection = [expected_number_zero, expected_number_one]

    assert expected_collection, [nil, 1].map(&Number)
    end

    def test_fn_component
    number = UI::Component.fn do
    strong { text(data) }
    end

    expected_number_zero = "<strong>0</strong>\n"

    assert expected_number_zero, number.call

    expected_number_one = "<strong>1</strong>\n"

    assert expected_number_one, number.call(1)

    expected_collection = [expected_number_zero, expected_number_one]

    assert expected_collection, [nil, 1].map(&number)
    end

    def test_component_composition
    container = UI::Component.fn { div data }

    number = UI::Component.fn do
    strong { text(data) }
    end

    assert "<div><strong>0</strong>\n</div>\n", container.call(number.call)

    assert "<div><strong>1</strong>\n</div>\n", container.call(number.(1))

    assert ["<div><strong>1</strong>\n</div>\n"], [1].map(&number).map(&container)
    end
    end

    # == test runner ==

    Microtest.call
    110 changes: 0 additions & 110 deletions ucomponent.rb
    Original file line number Diff line number Diff line change
    @@ -1,110 +0,0 @@
    class UComponent2
    require 'cgi'
    require 'nokogiri/html'

    module DataAccessor
    def data=(value)
    @data = value
    end

    def data; @data; end
    end

    def self.fn(&block)
    new(block)
    end

    def self.call(data = {})
    new.call(data)
    end

    def self.to_proc
    -> (data = {}) { call(data) }
    end

    def self.template(&block)
    define_method(:__template) { block }
    end

    def self.defaults(data = {})
    define_method(:__defaults) { data }
    end

    def initialize(fn = nil)
    @fn = fn || __template

    @defaults = respond_to?(:__defaults) ? __defaults : {}
    end

    def call(data)
    CGI.unescapeHTML(build_html_with(data))
    end

    def to_proc
    -> (data) { call(data) }
    end

    private

    def build_html_with(data)
    builder = Nokogiri::HTML::Builder.new
    builder.context = builder
    builder.extend(DataAccessor)
    builder.data = apply_defaults_to(data)
    builder.instance_eval(&@fn)
    builder.to_html.sub!(/<!DOCTYPE.*[^>]/, '')
    end

    def apply_defaults_to(data)
    return @defaults.merge(data) if data.is_a?(Hash)

    data || @defaults
    end
    end

    class HelloWorldV2 < UComponent2
    template do
    span.bar.foo! {
    text("Hello World")
    }
    end
    end

    HelloWorldV2.call

    class GreetV2 < UComponent2
    defaults name: 'John Doe'

    template do
    span.bar.foo! {
    text("Hello #{data[:name]}")
    }
    end
    end

    GreetV2.call

    GreetV2.call name: 'Rodrigo'

    [{name: 'Rodrigo'}, {name: 'Talita'}, {}].map(&GreetV2)

    class NumberV2 < UComponent2
    template do
    strong { text(data) }
    end
    end

    NumberV2.call 1

    [1,2,3,4].map(&NumberV2)

    number_v2 = UComponent2.fn do
    strong { text(data) }
    end

    number_v2.call 1

    [1,2,3,4].map(&number_v2)


    [1,2,3,4].map(&number_v2).map(&UComponent2.fn { div data })
    111 changes: 0 additions & 111 deletions ucomponent_v1.rb
    Original file line number Diff line number Diff line change
    @@ -1,111 +0,0 @@
    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!(/<!DOCTYPE.*[^>]/, '')
    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
    #=> "<span class=\"bold\">Hello world</span>\n"

    HelloWorld2.call message: 'Olá mundo'
    #=> "<span class=\"bold\">Ol&aacute; mundo</span>\n"

    UComponent.call -> doc { doc.span.bold { doc.text("Hello world") } }
    #=> "<span class=\"bold\">Hello world</span>\n"

    UComponent.call(
    -> doc, message { doc.span.bold { doc.text(message) } },
    'Olá mundo'
    )
    #=> "<span class=\"bold\">Ol&aacute; mundo</span>\n"

    Greet.call
    #=> "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"

    Greet.call name: 'Serradura'
    #=> "<span class=\"bar\" id=\"foo\">Hello Serradura</span>\n"

    [{name: 'Rodrigo'}, {name: 'Talita'}, {}].map(&Greet)
    #=> ["<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n", "<span class=\"bar\" id=\"foo\">Hello Talita</span>\n", "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"]

    greet = UComponent.new(-> (doc, name:) do
    doc.span.bar.foo! {
    doc.text("Hello #{name}")
    }
    end)

    greet.call(name: 'Bella')
    #=> "<span class=\"bar\" id=\"foo\">Hello Bella</span>\n"

    [{name: 'Rodrigo'}, {name: 'Talita'}].map(&greet)
    #=> ["<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n", "<span class=\"bar\" id=\"foo\">Hello Talita</span>\n"]

    number = UComponent.new(-> (doc, n) do
    doc.strong { doc.text(n) }
    end)

    [1,2,3,4].map(&number)
    #=> ["<strong>1</strong>\n", "<strong>2</strong>\n", "<strong>3</strong>\n", "<strong>4</strong>\n"]
  8. @serradura serradura revised this gist Feb 10, 2019. 1 changed file with 17 additions and 7 deletions.
    24 changes: 17 additions & 7 deletions ucomponent.rb
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,7 @@
    class UComponent2
    require 'cgi'
    require 'nokogiri/html'

    module DataAccessor
    def data=(value)
    @data = value
    @@ -34,12 +37,7 @@ def initialize(fn = nil)
    end

    def call(data)
    builder = Nokogiri::HTML::Builder.new
    builder.context = @builder
    builder.extend(DataAccessor)
    builder.data = apply_defaults_to(data)
    builder.instance_eval(&@fn)
    builder.to_html.sub!(/<!DOCTYPE.*[^>]/, '')
    CGI.unescapeHTML(build_html_with(data))
    end

    def to_proc
    @@ -48,6 +46,15 @@ def to_proc

    private

    def build_html_with(data)
    builder = Nokogiri::HTML::Builder.new
    builder.context = builder
    builder.extend(DataAccessor)
    builder.data = apply_defaults_to(data)
    builder.instance_eval(&@fn)
    builder.to_html.sub!(/<!DOCTYPE.*[^>]/, '')
    end

    def apply_defaults_to(data)
    return @defaults.merge(data) if data.is_a?(Hash)

    @@ -97,4 +104,7 @@ class NumberV2 < UComponent2

    number_v2.call 1

    [1,2,3,4].map(&number_v2)
    [1,2,3,4].map(&number_v2)


    [1,2,3,4].map(&number_v2).map(&UComponent2.fn { div data })
  9. @serradura serradura revised this gist Feb 10, 2019. 2 changed files with 173 additions and 73 deletions.
    135 changes: 62 additions & 73 deletions ucomponent.rb
    Original file line number Diff line number Diff line change
    @@ -1,111 +1,100 @@
    class UComponent
    def self.defaults
    {}
    end
    class UComponent2
    module DataAccessor
    def data=(value)
    @data = value
    end

    def self.build(_doc, _options)
    raise NotImplementedError
    def data; @data; end
    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] || {})
    def self.fn(&block)
    new(block)
    end

    new.call(options, fn)
    def self.call(data = {})
    new.call(data)
    end

    def self.to_proc
    -> (*args) { call(*args) }
    -> (data = {}) { call(data) }
    end

    def initialize(fn = nil)
    @fn = fn
    def self.template(&block)
    define_method(:__template) { block }
    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
    def self.defaults(data = {})
    define_method(:__defaults) { data }
    end

    def initialize(fn = nil)
    @fn = fn || __template

    @defaults = respond_to?(:__defaults) ? __defaults : {}
    end

    def call(data)
    builder = Nokogiri::HTML::Builder.new
    builder.context = @builder
    builder.extend(DataAccessor)
    builder.data = apply_defaults_to(data)
    builder.instance_eval(&@fn)
    builder.to_html.sub!(/<!DOCTYPE.*[^>]/, '')
    end

    def to_proc
    -> (options) { call(options) }
    -> (data) { call(data) }
    end
    end

    class HelloWorld < UComponent
    def self.build(doc)
    doc.span.bold {
    doc.text("Hello world")
    }
    private

    def apply_defaults_to(data)
    return @defaults.merge(data) if data.is_a?(Hash)

    data || @defaults
    end
    end

    class HelloWorld2 < UComponent
    def self.build(doc, message:)
    doc.span.bold {
    doc.text(message)
    class HelloWorldV2 < UComponent2
    template do
    span.bar.foo! {
    text("Hello World")
    }
    end
    end

    class Greet < UComponent
    def self.defaults
    { name: 'John Doe' }
    end
    HelloWorldV2.call

    def self.build(doc, name:)
    doc.span.bar.foo! {
    doc.text("Hello #{name}")
    class GreetV2 < UComponent2
    defaults name: 'John Doe'

    template do
    span.bar.foo! {
    text("Hello #{data[:name]}")
    }
    end
    end

    # == Examples ==

    HelloWorld.call
    #=> "<span class=\"bold\">Hello world</span>\n"

    HelloWorld2.call message: 'Olá mundo'
    #=> "<span class=\"bold\">Ol&aacute; mundo</span>\n"
    GreetV2.call

    UComponent.call -> doc { doc.span.bold { doc.text("Hello world") } }
    #=> "<span class=\"bold\">Hello world</span>\n"
    GreetV2.call name: 'Rodrigo'

    UComponent.call(
    -> doc, message { doc.span.bold { doc.text(message) } },
    'Olá mundo'
    )
    #=> "<span class=\"bold\">Ol&aacute; mundo</span>\n"
    [{name: 'Rodrigo'}, {name: 'Talita'}, {}].map(&GreetV2)

    Greet.call
    #=> "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"

    Greet.call name: 'Serradura'
    #=> "<span class=\"bar\" id=\"foo\">Hello Serradura</span>\n"

    [{name: 'Rodrigo'}, {name: 'Talita'}, {}].map(&Greet)
    #=> ["<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n", "<span class=\"bar\" id=\"foo\">Hello Talita</span>\n", "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"]
    class NumberV2 < UComponent2
    template do
    strong { text(data) }
    end
    end

    greet = UComponent.new(-> (doc, name:) do
    doc.span.bar.foo! {
    doc.text("Hello #{name}")
    }
    end)
    NumberV2.call 1

    greet.call(name: 'Bella')
    #=> "<span class=\"bar\" id=\"foo\">Hello Bella</span>\n"
    [1,2,3,4].map(&NumberV2)

    [{name: 'Rodrigo'}, {name: 'Talita'}].map(&greet)
    #=> ["<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n", "<span class=\"bar\" id=\"foo\">Hello Talita</span>\n"]
    number_v2 = UComponent2.fn do
    strong { text(data) }
    end

    number = UComponent.new(-> (doc, n) do
    doc.strong { doc.text(n) }
    end)
    number_v2.call 1

    [1,2,3,4].map(&number)
    #=> ["<strong>1</strong>\n", "<strong>2</strong>\n", "<strong>3</strong>\n", "<strong>4</strong>\n"]
    [1,2,3,4].map(&number_v2)
    111 changes: 111 additions & 0 deletions ucomponent_v1.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,111 @@
    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!(/<!DOCTYPE.*[^>]/, '')
    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
    #=> "<span class=\"bold\">Hello world</span>\n"

    HelloWorld2.call message: 'Olá mundo'
    #=> "<span class=\"bold\">Ol&aacute; mundo</span>\n"

    UComponent.call -> doc { doc.span.bold { doc.text("Hello world") } }
    #=> "<span class=\"bold\">Hello world</span>\n"

    UComponent.call(
    -> doc, message { doc.span.bold { doc.text(message) } },
    'Olá mundo'
    )
    #=> "<span class=\"bold\">Ol&aacute; mundo</span>\n"

    Greet.call
    #=> "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"

    Greet.call name: 'Serradura'
    #=> "<span class=\"bar\" id=\"foo\">Hello Serradura</span>\n"

    [{name: 'Rodrigo'}, {name: 'Talita'}, {}].map(&Greet)
    #=> ["<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n", "<span class=\"bar\" id=\"foo\">Hello Talita</span>\n", "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"]

    greet = UComponent.new(-> (doc, name:) do
    doc.span.bar.foo! {
    doc.text("Hello #{name}")
    }
    end)

    greet.call(name: 'Bella')
    #=> "<span class=\"bar\" id=\"foo\">Hello Bella</span>\n"

    [{name: 'Rodrigo'}, {name: 'Talita'}].map(&greet)
    #=> ["<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n", "<span class=\"bar\" id=\"foo\">Hello Talita</span>\n"]

    number = UComponent.new(-> (doc, n) do
    doc.strong { doc.text(n) }
    end)

    [1,2,3,4].map(&number)
    #=> ["<strong>1</strong>\n", "<strong>2</strong>\n", "<strong>3</strong>\n", "<strong>4</strong>\n"]
  10. @serradura serradura revised this gist Feb 9, 2019. 1 changed file with 45 additions and 11 deletions.
    56 changes: 45 additions & 11 deletions ucomponent.rb
    Original file line number Diff line number Diff line change
    @@ -1,33 +1,40 @@
    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.defaults
    {}
    end

    def self.build(_doc, _options)
    raise NotImplementedError
    def self.to_proc
    -> (*args) { call(*args) }
    end

    def initialize(fn = nil)
    @fn = fn
    end

    def call(options = {}, fn = nil)
    def call(opt = {}, fn = nil)
    func = @fn || fn
    func_options = self.class.defaults.merge(options)

    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, func_options)
    func.arity == 1 ? func.(doc) : func.(doc, options)
    end

    builder.to_html.sub!(/<!DOCTYPE.*[^>]/, '')
    end

    def to_proc
    -> (options) { call(options) }
    end
    end

    class HelloWorld < UComponent
    @@ -58,20 +65,47 @@ def self.build(doc, name:)
    end
    end

    # == Examples ==

    HelloWorld.call
    #=> "<span class=\"bold\">Hello world</span>\n"

    HelloWorld2.call message: 'Olá mundo'
    #=> "<span class=\"bold\">Ol&aacute; mundo</span>\n"

    UComponent.call -> doc { doc.span.bold { doc.text("Hello world") } }
    #=> "<span class=\"bold\">Hello world</span>\n"

    UComponent.call(
    -> doc, message { doc.span.bold { doc.text(message) } },
    'Olá mundo'
    )
    #=> "<span class=\"bold\">Ol&aacute; mundo</span>\n"

    Greet.call
    #=> "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"

    Greet.call name: 'Serradura'
    #=> "<span class=\"bar\" id=\"foo\">Hello Serradura</span>\n"

    [{name: 'Rodrigo'}, {name: 'Talita'}, {}].map(&Greet)
    #=> ["<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n", "<span class=\"bar\" id=\"foo\">Hello Talita</span>\n", "<span class=\"bar\" id=\"foo\">Hello John Doe</span>\n"]

    greet = UComponent.new(-> (doc, name:) do
    doc.span.bar.foo! {
    doc.text("Hello #{name}")
    }
    end)

    greet.call(name: 'Bella')
    greet.call(name: 'Bella')
    #=> "<span class=\"bar\" id=\"foo\">Hello Bella</span>\n"

    [{name: 'Rodrigo'}, {name: 'Talita'}].map(&greet)
    #=> ["<span class=\"bar\" id=\"foo\">Hello Rodrigo</span>\n", "<span class=\"bar\" id=\"foo\">Hello Talita</span>\n"]

    number = UComponent.new(-> (doc, n) do
    doc.strong { doc.text(n) }
    end)

    [1,2,3,4].map(&number)
    #=> ["<strong>1</strong>\n", "<strong>2</strong>\n", "<strong>3</strong>\n", "<strong>4</strong>\n"]
  11. @serradura serradura created this gist Feb 9, 2019.
    77 changes: 77 additions & 0 deletions ucomponent.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    class UComponent
    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.defaults
    {}
    end

    def self.build(_doc, _options)
    raise NotImplementedError
    end

    def initialize(fn = nil)
    @fn = fn
    end

    def call(options = {}, fn = nil)
    func = @fn || fn
    func_options = self.class.defaults.merge(options)

    builder = Nokogiri::HTML::Builder.new do |doc|
    func.arity == 1 ? func.(doc) : func.(doc, func_options)
    end

    builder.to_html.sub!(/<!DOCTYPE.*[^>]/, '')
    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

    HelloWorld.call

    HelloWorld2.call message: 'Olá mundo'

    UComponent.call -> doc { doc.span.bold { doc.text("Hello world") } }

    Greet.call

    Greet.call name: 'Serradura'

    greet = UComponent.new(-> (doc, name:) do
    doc.span.bar.foo! {
    doc.text("Hello #{name}")
    }
    end)

    greet.call(name: 'Bella')