Skip to content

Commit

Permalink
Merge pull request #277 from scarpe-team/shoes_api_self
Browse files Browse the repository at this point in the history
Use a single Scarpe::App instance as self nearly everywhere
  • Loading branch information
noahgibbs authored Jul 5, 2023
2 parents 8e931fe + 5481f38 commit e84e321
Show file tree
Hide file tree
Showing 21 changed files with 285 additions and 200 deletions.
18 changes: 18 additions & 0 deletions examples/selfitude.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
def printish(msg)
#$stderr.puts msg
File.open("/tmp/shoesy_stuff.txt", "a") do |f|
f.write(msg + "\n")
end
end

Shoes.app do
printish "Self top: #{self.inspect}" # It's an instance of Shoes::App, yes
stack do
printish "Self in stack: #{self.inspect}" # Yup, here too
end
button("Clickity") do
alert("You clicked me!")
printish "Self in button handler: #{self.inspect}" # And here too
end
end

90 changes: 87 additions & 3 deletions lib/scarpe/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,24 @@ def initialize(
debug: ENV["SCARPE_DEBUG"] ? true : false,
&app_code_body
)
log_init("Scarpe::App")

if Scarpe::App.instance
@log.error("Trying to create a second Scarpe::App in the same process! Fail!")
raise "Cannot create multiple Scarpe::App objects!"
else
Scarpe::App.instance = self
end

log_init("Scarpe::App")

@do_shutdown = false

super

# This creates the DocumentRoot, including its corresponding display widget
@document_root = Scarpe::DocumentRoot.new

@slots = []

# Now create the App display widget
create_display_widget

Expand Down Expand Up @@ -68,7 +70,31 @@ def init
send_shoes_event(event_name: "init")
return if @do_shutdown

@document_root.instance_eval(&@app_code_body)
::Scarpe::App.instance.with_slot(@document_root, &@app_code_body)
end

# "Container" widgets like flows, stacks, masks and the document root
# are considered "slots" in Scarpe parlance. When a new slot is created,
# we push it here in order to track what widgets are found in that slot.
def push_slot(slot)
@slots.push(slot)
end

def pop_slot
@slots.pop
end

def current_slot
@slots[-1]
end

def with_slot(slot_item, &block)
return unless block_given?

push_slot(slot_item)
Scarpe::App.instance.instance_eval(&block)
ensure
pop_slot
end

# This isn't supposed to return. The display service should take control
Expand All @@ -85,5 +111,63 @@ def destroy(send_event: true)
@do_shutdown = true
send_shoes_event(event_name: "destroy") if send_event
end

def all_widgets
out = []

to_add = @document_root.children
until to_add.empty?
out.concat(to_add)
to_add = to_add.flat_map(&:children).compact
end

out
end

# We can add various ways to find widgets here.
# These are sort of like Shoes selectors, used for testing.
def find_widgets_by(*specs)
widgets = all_widgets
specs.each do |spec|
if spec.is_a?(Class)
widgets.select! { |w| spec === w }
elsif spec.is_a?(Symbol)
s = spec.to_s
case s[0]
when "$"
begin
# I'm not finding a global_variable_get or similar...
global_value = eval s
widgets &= [global_value]
rescue
raise "Error getting global variable: #{spec.inspect}"
end
when "@"
if app.instance_variables.include?(spec)
widgets &= [self.instance_variable_get(spec)]
else
raise "Can't find top-level instance variable: #{spec.inspect}!"
end
else
end
else
raise("Don't know how to find widgets by #{spec.inspect}!")
end
end
widgets
end
end
end

# DSL methods
class Scarpe::App
def background(...)
current_slot.background(...)
end

def border(...)
current_slot.border(...)
end

alias_method :info, :puts
end
4 changes: 2 additions & 2 deletions lib/scarpe/cats_cradle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,9 @@ def on_event(event, &block)
# Also, wait, what's up with span? What *is* that?
WIDGET_FINDERS.each do |finder_name, scarpe_class|
define_method(finder_name) do |*args|
root = Scarpe::App.instance.document_root
app = Scarpe::App.instance

widgets = root.find_widgets_by(scarpe_class, *args)
widgets = app.find_widgets_by(scarpe_class, *args)
raise "Found more than one #{finder_name} matching #{args.inspect}!" if widgets.size > 1
raise "Found no #{finder_name} matching #{args.inspect}!" if widgets.empty?

Expand Down
54 changes: 2 additions & 52 deletions lib/scarpe/document_root.rb
Original file line number Diff line number Diff line change
@@ -1,66 +1,16 @@
# frozen_string_literal: true

class Scarpe
class DocumentRoot < Scarpe::Widget
include Scarpe::Background

class DocumentRoot < Scarpe::Slot
def initialize
super

create_display_widget
end

# This can be absolutely huge in console output, and it's frequently printed.
# The default inspect string can be absolutely huge in console output, and it's frequently printed.
def inspect
"<Scarpe::DocumentRoot>"
end

alias_method :info, :puts

def all_widgets
out = []

to_add = self.children
until to_add.empty?
out.concat(to_add)
to_add = to_add.flat_map(&:children).compact
end

out
end

# We can add various ways to find widgets here.
# These are sort of like Shoes selectors, used for testing.
def find_widgets_by(*specs)
widgets = all_widgets
app = Scarpe::App.instance
specs.each do |spec|
if spec.is_a?(Class)
widgets.select! { |w| spec === w }
elsif spec.is_a?(Symbol)
s = spec.to_s
case s[0]
when "$"
begin
# I'm not finding a global_variable_get or similar...
global_value = eval s
widgets = [global_value]
rescue
raise "Error getting global variable: #{spec.inspect}"
end
when "@"
if app.instance_variables.include?(spec)
widgets = [app.instance_variable_get(spec)]
else
raise "Can't find top-level instance variable: #{spec.inspect}!"
end
else
end
else
raise("Don't know how to find widgets by #{spec.inspect}!")
end
end
widgets
end
end
end
47 changes: 47 additions & 0 deletions lib/scarpe/download.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

require "net/http"
require "openssl"
require "nokogiri"

class Scarpe::Widget
def download(url)
Thread.new do
begin
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri.request_uri)

http.request(request) do |response|
case response
when Net::HTTPSuccess
# content = response.body

# headers = response.header

html_get_title(content)
else
Scarpe.error("Failed to download content. Response code: #{response.code}")
end
end
rescue StandardError => e
Scarpe.error("Error occurred while downloading: #{e.message}")
end
end
end

private

def html_get_title(content)
doc = Nokogiri::HTML(content)

title = doc.at_css("title")&.text&.strip || ""

# headings = doc.css("h1").map(&:text)

title
end
end
7 changes: 2 additions & 5 deletions lib/scarpe/flow.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
# frozen_string_literal: true

class Scarpe
class Flow < Scarpe::Widget
include Scarpe::Background
include Scarpe::Border

class Flow < Scarpe::Slot
display_properties :width, :height, :margin, :padding

def initialize(width: nil, height: "100%", margin: nil, padding: nil, &block)
Expand All @@ -13,7 +10,7 @@ def initialize(width: nil, height: "100%", margin: nil, padding: nil, &block)
# Create the display-side widget *before* instance_eval, which will add child widgets with their display widgets
create_display_widget

instance_eval(&block)
Scarpe::App.instance.with_slot(self, &block) if block_given?
end
end
end
1 change: 1 addition & 0 deletions lib/scarpe/shape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def initialize(left: nil, top: nil, path_commands: nil, &block)

super()
create_display_widget

instance_eval(&block) if block_given?
end
end
Expand Down
6 changes: 6 additions & 0 deletions lib/scarpe/slot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

class Scarpe::Slot < Scarpe::Widget
include Scarpe::Background
include Scarpe::Border
end
54 changes: 3 additions & 51 deletions lib/scarpe/stack.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
# frozen_string_literal: true

require "net/http"
require "openssl"
require "nokogiri"

class Scarpe
class Stack < Scarpe::Widget
include Scarpe::Background
include Scarpe::Border
class Stack < Scarpe::Slot
include Scarpe::Spacing

display_properties :width, :height, :margin, :padding, :scroll, :margin_top, :margin_left, :margin_right, :margin_bottom, :options
Expand All @@ -21,50 +15,8 @@ def initialize(width: nil, height: "100%", margin: nil, padding: nil, scroll: fa
super

create_display_widget
# Create the display-side widget *before* instance_eval, which will add child widgets with their display widgets
instance_eval(&block) if block_given?
end
end

class Widget
def download(url)
Thread.new do
begin
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(uri.request_uri)

http.request(request) do |response|
case response
when Net::HTTPSuccess
# content = response.body

# headers = response.header

get_title(content)
else
Scarpe.error("Failed to download content. Response code: #{response.code}")
end
end
rescue StandardError => e
Scarpe.error("Error occurred while downloading: #{e.message}")
end
end
end

private

def get_title(content)
doc = Nokogiri::HTML(content)

title = doc.at_css("title")&.text&.strip || ""

# headings = doc.css("h1").map(&:text)

title
# Create the display-side widget *before* running the block, which will add child widgets with their display widgets
Scarpe::App.instance.with_slot(self, &block) if block_given?
end
end
end
Loading

0 comments on commit e84e321

Please sign in to comment.