Skip to content

Instantly share code, notes, and snippets.

@delonnewman
Created January 21, 2021 23:19
Show Gist options
  • Save delonnewman/d0d5f3607bd6b3ac0603b8894da05b40 to your computer and use it in GitHub Desktop.
Save delonnewman/d0d5f3607bd6b3ac0603b8894da05b40 to your computer and use it in GitHub Desktop.
require 'stringio'
require 'erb'
class Event
end
class MorphList
include Enumerable
def self.empty
new([])
end
def initialize(morphs)
@morphs = morphs
end
def each(&block)
@morphs.each(&block)
self
end
def remove(morph)
@morphs.delete(morph)
end
def <<(morph)
@morphs << morph
end
def size
@morphs.size
end
end
class SubmorphEvent < Event
attr_reader :owner, :morph
def initialize(owner:, morph:)
@owner = owner
@morph = morph
end
end
class MorphRemovedEvent < SubmorphEvent
def call
owner.world.render
end
end
class MorphAddedEvent < SubmorphEvent
def call
owner.world.render
end
end
class Morph
include Enumerable
attr_reader :submorphs
attr_accessor :owner, :world, :name, :position, :extent
def initialize(name: nil, position: nil, extent: nil, owner: nil, world: nil, submorphs: MorphList.empty)
@ower = owner if owner
@world = world if world
@name = name if name
@position = position if position
@extent = extent if extent
@submorphs = submorphs
@events = []
end
def in_world?
[email protected]?
end
def submorph_count
@submorphs.size
end
def each_submorph(&block)
@submorphs.each(&block)
self
end
alias_method :each, :each_submorph
def each_morph(&block)
block.call(self)
each_submorph(&block)
end
def delete!
owner.remove_morph(self) if owner
self
end
def add_morph!(morph)
@submorphs << morph
morph.owner = self
morph.world = world
trigger_event!(MorphAddedEvent.new(owner: self, morph: morph))
end
def remove_morph!(morph)
morph.owner = nil
@submorphs.remove(morph)
trigger_event!(MorphRemovedEvent.new(owner: self, morph: morph))
end
def trigger_event!(event)
@events << event
end
def proccess_events_now
events = @events.dup
@events = []
events.each do |event|
event.call
end
end
def copy
new(owner: @owner, world: @world)
end
def deep_copy
new(owner: @owner, world: @world, submorphs: @submorphs)
end
def render
raise 'not implmented'
end
end
class Point
def self.[](x, y)
new(x: x, y: y)
end
def initialize(x:, y:)
@x = x
@y = y
end
def to_s
"#{x}@#{y}"
end
alias_method :inspect, :to_s
end
class WorldState < Morph
def initialize(**args)
super(**args.merge(world: self))
end
def render
reduce(StringIO.new) do |buffer, morph|
buffer.write(morph.render)
buffer
end.string
end
end
class HTML5Document < Morph
TEMPLATE = <<~HTML
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
<title><%= title %></title>
</head>
<body>
<% each_submorph do |morph| %>
<%= morph.render %>
<% end %>
<!-- Optional JavaScript; choose one of the two! -->
<!-- Option 1: Bootstrap Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
<!-- Option 2: Separate Popper and Bootstrap JS -->
<!--
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>
-->
</body>
</html>
HTML
attr_reader :title
def intialize(title:)
@title = title
end
def render
ERB.new(TEMPLATE).result(binding)
end
end
module Bootstrap5
class Container < Morph
def render
buffer = StringIO.new
each_submorph do |morph|
buffer.write(morph.render)
end
"<div class=\"container\">#{buffer.string}</div>"
end
end
class Row < Morph
def render
end
end
class Element < Morph
def element_id
"element-#{object_id}"
end
def render
"<div id=\"#{element_id}\">#{name || element_id}</div>"
end
end
end
World = WorldState.new
document = HTML5Document.new(title: 'Test Application')
World.add_morph!(document)
container = Bootstrap5::Container.new
container.add_morph!(Bootstrap5::Element.new(name: 'test'))
document.add_morph!(container)
puts World.render
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment