such 2.0.210201 → 2.1.230106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +15 -18
- data/lib/such/convention.rb +99 -0
- data/lib/such/part.rb +11 -2
- data/lib/such/parts.rb +28 -7
- data/lib/such/such.rb +10 -5
- data/lib/such/thing.rb +11 -7
- data/lib/such/things.rb +9 -8
- data/lib/such.rb +7 -6
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c296ea57df2d19bf9db70eb35f97494e15b8c739d5b0e8cea5a31163c457dc76
|
4
|
+
data.tar.gz: a1c10cf419d67311a9b9e33fdf26a3f587052af4a5a3260a6b3f2db90b255056
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23fd64fe227f2fb0fd3e3ac26c512a754fbf19b047a90ca21e0e12bc807309642543e614cc33b88a5aee46c6efe2f06fc4ea06d7881cf59d25346c3acd44af1d
|
7
|
+
data.tar.gz: 8907935ccdea95e257bda042ac851fdcacdb0d9b8972e8f43d1d4fd1e071d514f59001d7e5557c0d864b4f17c6813e1d77b3a2047a20b866c032bf14b3766723
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Such
|
2
2
|
|
3
|
-
* [VERSION 2.
|
3
|
+
* [VERSION 2.1.230106](https://github.com/carlosjhr64/such/releases)
|
4
4
|
* [github](https://www.github.com/carlosjhr64/such)
|
5
5
|
* [rubygems](https://rubygems.org/gems/such)
|
6
6
|
|
@@ -12,13 +12,10 @@ Can be used to wrap any class with the alternate constructor,
|
|
12
12
|
although targeted only Gtk3 widgets.
|
13
13
|
|
14
14
|
## INSTALL:
|
15
|
-
|
16
15
|
```shell
|
17
|
-
$
|
16
|
+
$ gem install such
|
18
17
|
```
|
19
|
-
|
20
18
|
## SYNOPSIS:
|
21
|
-
|
22
19
|
```ruby
|
23
20
|
require 'gtk3'
|
24
21
|
require 'such'
|
@@ -38,13 +35,11 @@ window.show_all
|
|
38
35
|
|
39
36
|
Gtk.main #=> nil
|
40
37
|
```
|
41
|
-
|
42
38
|
## MORE:
|
43
39
|
|
44
|
-
Arrays are passed to
|
40
|
+
Arrays are passed to constructor's super,
|
45
41
|
Hashes are method=>arguments pairs, and Strings are signals.
|
46
42
|
Other objects are assumed to be containers:
|
47
|
-
|
48
43
|
```ruby
|
49
44
|
Such::Button.new(
|
50
45
|
window,
|
@@ -60,17 +55,13 @@ window.add button
|
|
60
55
|
button.set_size_request 100, 50
|
61
56
|
button.signal_connect('clicked'){puts 'OK'}
|
62
57
|
```
|
63
|
-
|
64
58
|
To set the packing method to say `:pack_start`, set the `:into` method as follows:
|
65
|
-
|
66
59
|
```ruby
|
67
60
|
{into: [:pack_start, expand:false, fill:false, padding:0]}
|
68
|
-
# The effect in the
|
61
|
+
# The effect in the constructor will be as if the following was run:
|
69
62
|
# container.pack_start(self, expand:false, fill:false, padding:0)
|
70
63
|
```
|
71
|
-
|
72
64
|
One can configure Symbol keys to represent metadata about a widget:
|
73
|
-
|
74
65
|
```ruby
|
75
66
|
Thing.configure(
|
76
67
|
KEY: [ arg1, arg2, arg3 ], # an array for super(arg1, arg2, arg3)
|
@@ -82,8 +73,7 @@ One can configure Symbol keys to represent metadata about a widget:
|
|
82
73
|
key!: [[arg1, arg2], {meth1:args1, meth2:args2}, 'signal1', 'signal2'] # the splatter bang!
|
83
74
|
)
|
84
75
|
```
|
85
|
-
|
86
|
-
The examples in this repository are reworks of the examples given in
|
76
|
+
Many examples in this repository are reworks of the examples given in
|
87
77
|
ZetCode.com[http://zetcode.com/gui/rubygtk/] (back in 2015).
|
88
78
|
|
89
79
|
## Features:
|
@@ -93,19 +83,26 @@ ZetCode.com[http://zetcode.com/gui/rubygtk/] (back in 2015).
|
|
93
83
|
* Missing signal assumed to be 'clicked'
|
94
84
|
* Heuristics on when one wants to iterate a method over the arguments given
|
95
85
|
* Packing method defaults (ultimately) to :add
|
96
|
-
* Way to change default packing
|
86
|
+
* Way to change default packing behavior
|
97
87
|
|
98
88
|
## But wait! One more thing:
|
99
89
|
|
100
90
|
See [such_parts_demo](examples/such_parts_demo) in the examples directory
|
101
|
-
and [tc_part](test/tc_part) for hints on how to use
|
91
|
+
and [tc_part](test/tc_part) for hints on how to use
|
102
92
|
[Such::Part module](lib/such/part.rb).
|
103
93
|
|
94
|
+
See also [tc_convention](test/tc_convention) in the test directory
|
95
|
+
for a suggested convention for naming keys.
|
96
|
+
|
97
|
+
## Gnarly!
|
98
|
+
|
99
|
+
Yes, I know!
|
100
|
+
|
104
101
|
## LICENSE:
|
105
102
|
|
106
103
|
(The MIT License)
|
107
104
|
|
108
|
-
Copyright (c)
|
105
|
+
Copyright (c) 2023 CarlosJHR64
|
109
106
|
|
110
107
|
Permission is hereby granted, free of charge, to any person obtaining
|
111
108
|
a copy of this software and associated documentation files (the
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Use this to validate your configuration against the given convention.
|
2
|
+
# It should be run as part of your tests,
|
3
|
+
# not necessarily in your application.
|
4
|
+
module Such
|
5
|
+
module Convention
|
6
|
+
module Refinements
|
7
|
+
refine Object do
|
8
|
+
# Atoms
|
9
|
+
def item_symbol?
|
10
|
+
is_a?Symbol and match?(/^\w+[!?]?$/)
|
11
|
+
end
|
12
|
+
def item_boolean?
|
13
|
+
[TrueClass,FalseClass].any?{is_a?_1}
|
14
|
+
end
|
15
|
+
def item_value?
|
16
|
+
[String,Float,Integer].any?{is_a?_1}
|
17
|
+
end
|
18
|
+
# Item
|
19
|
+
def item_tangible?
|
20
|
+
item_value? or item_symbol?
|
21
|
+
end
|
22
|
+
def item?
|
23
|
+
item_tangible? or item_boolean?
|
24
|
+
end
|
25
|
+
# Items
|
26
|
+
def items?
|
27
|
+
item? or item_array? or item_hash?
|
28
|
+
end
|
29
|
+
def item_hash?
|
30
|
+
is_a?Hash and all?{_1.item_symbol? and (_2.nil? or _2.items?)}
|
31
|
+
end
|
32
|
+
def item_array?
|
33
|
+
is_a?Array and all?{_1.nil? or _1.items?}
|
34
|
+
end
|
35
|
+
def item_bang_array?
|
36
|
+
is_a?Array and
|
37
|
+
all?{_1.is_a?Symbol or _1.is_a?String or
|
38
|
+
_1.item_array? or _1.item_hash?}
|
39
|
+
end
|
40
|
+
def item_explicit_array?
|
41
|
+
is_a?Array and
|
42
|
+
all?{_1.is_a?String or _1.item_array? or _1.item_hash?}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
CAPS = /^[A-Z_]+$/
|
48
|
+
CAPS1 = /^[A-Z_]+!$/
|
49
|
+
CAPS2 = /^[A-Z_]+[?]$/
|
50
|
+
LOWER = /^[a-z_]+$/
|
51
|
+
LOWER1 = /^[a-z_]+!$/
|
52
|
+
LOWER2 = /^[a-z_]+[?]$/
|
53
|
+
WORD = /^\w+$/
|
54
|
+
WORD1 = /^\w+!$/
|
55
|
+
WORD2 = /^\w+[?]$/
|
56
|
+
|
57
|
+
using Refinements
|
58
|
+
|
59
|
+
def self.validate_kv(k,v)
|
60
|
+
raise 'unrecognized item symbol' unless k.item_symbol?
|
61
|
+
case k
|
62
|
+
when LOWER # abc
|
63
|
+
raise 'unrecognized item hash' unless v.item_hash? or
|
64
|
+
(v.is_a?Symbol and LOWER.match?v)
|
65
|
+
when CAPS # ABC
|
66
|
+
raise 'unrecognized item array' unless v.item_array?
|
67
|
+
when WORD # Abc
|
68
|
+
raise 'unrecognized item tangible' unless v.item_tangible?
|
69
|
+
when LOWER1 # abc!
|
70
|
+
raise 'unrecognized bang array' unless v.item_bang_array?
|
71
|
+
when CAPS1 # ABC!
|
72
|
+
raise 'unrecognized explicit array' unless v.item_explicit_array?
|
73
|
+
when WORD1 # Abc!
|
74
|
+
raise 'unrecognized items' unless v.items?
|
75
|
+
when LOWER2 # abc?
|
76
|
+
raise 'unrecognized boolean' unless v.item_boolean?
|
77
|
+
when CAPS2 # ABC?
|
78
|
+
raise 'unrecognized boolean|nil' unless v.nil? or v.item_boolean?
|
79
|
+
when WORD2 # Abc?
|
80
|
+
raise 'unrecognized item value|nil' unless v.nil? or v.item_tangible?
|
81
|
+
else
|
82
|
+
raise 'should not happen'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.validate(config)
|
87
|
+
raise 'expected hash' unless config.is_a?Hash
|
88
|
+
errors = {}
|
89
|
+
config.each do |k,v|
|
90
|
+
begin
|
91
|
+
validate_kv(k,v)
|
92
|
+
rescue
|
93
|
+
errors[k] = $!.message
|
94
|
+
end
|
95
|
+
end
|
96
|
+
return errors.empty? ? nil : errors
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/such/part.rb
CHANGED
@@ -6,18 +6,27 @@ module Such
|
|
6
6
|
super(*parameters)
|
7
7
|
self.class.plugs.each do |plg|
|
8
8
|
if md = PLUG_PATTERN.match(plg)
|
9
|
-
plg,
|
9
|
+
plg,sym,cls = "#{plg}=".to_sym,
|
10
|
+
"#{md[:sym]}!".to_sym,
|
11
|
+
Object.const_get("Such::#{md[:cls]}")
|
10
12
|
# self.<plg> = Such::<cls>.new(self, :<sym>!, &block)
|
11
13
|
public_send plg, cls.new(self, sym, &block)
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
18
|
+
# Each part\Such::Part can invoke `message` on each of its connected part,
|
19
|
+
# and so on...
|
20
|
+
# Any part can override its `message` method to act as a terminal node or
|
21
|
+
# modify its relay behavior.
|
16
22
|
def message(*parameters)
|
17
23
|
self.class.plugs.each{|plg| public_send(plg).message(*parameters) }
|
18
24
|
end
|
19
25
|
|
20
|
-
|
26
|
+
# Maybe a plug down the plugged things responds?
|
27
|
+
# This is a search through the connections which
|
28
|
+
# returns the first(non-nil) response.
|
29
|
+
def method_missing(maybe,*args)
|
21
30
|
super unless args.length==0 and PLUG_PATTERN.match?(maybe)
|
22
31
|
self.class.plugs.each do |plug|
|
23
32
|
thing = public_send(plug)
|
data/lib/such/parts.rb
CHANGED
@@ -1,22 +1,43 @@
|
|
1
1
|
module Such
|
2
2
|
module Parts
|
3
|
+
### Such::Parts ###
|
4
|
+
# Signature:
|
5
|
+
# Such::Parts.make(part\Symbol, thing\Such::Thing, plugs\Array(Symbol)) \
|
6
|
+
# (Such::Thing+Such::Part)
|
7
|
+
# The name of the Such::Part sub-class will be part.
|
8
|
+
# It will be a sub-class of thing(a sub-class of Such::Thing).
|
9
|
+
# The Array of symbols, plugs, will be accessors to other connected Such::Part's.
|
10
|
+
# Each plug must match:
|
11
|
+
# /^<Part config key!>_<Name of connected sub-classed super-class>$/
|
12
|
+
# Note that these plugs are `attr_accessor`s,.
|
13
|
+
# Exemplar:
|
14
|
+
# Component0#initialize(...,&block_given)
|
15
|
+
# ...
|
16
|
+
# part_obj.key_Component1 = Component1.new(:key!,&block_given)
|
17
|
+
# ...
|
18
|
+
# Note that all connected components share the same block_given:
|
19
|
+
# block_given(part\Such::Part, *w, signal\String)
|
20
|
+
# It gets called when the part receives its assigned activation signal,
|
21
|
+
# like "clicked" for a Button.
|
3
22
|
def self.make(part, thing, *plugs)
|
4
23
|
unless thing < Such::Thing and [part,*plugs].all?{_1.is_a? Symbol}
|
5
|
-
raise "Expected Such::Parts.make(Symbol part,
|
24
|
+
raise "Expected Such::Parts.make(Symbol part, " +
|
25
|
+
"Class thing < Such::Thing, *Symbol plugs)"
|
6
26
|
end
|
7
27
|
plugs.each do |plug|
|
8
|
-
if /^[^\W_]+_(?<
|
28
|
+
if /^[^\W_]+_(?<super_class>[^\W_]+)$/=~plug
|
9
29
|
next unless $VERBOSE
|
10
|
-
unless Object.const_defined?("Such::#{
|
11
|
-
$stderr.puts "Warning: Such::#{
|
30
|
+
unless Object.const_defined?("Such::#{super_class}")
|
31
|
+
$stderr.puts "Warning: Such::#{super_class} not defined yet."
|
12
32
|
end
|
13
33
|
else
|
14
34
|
raise "Plugs must have the form key_class: #{plug}"
|
15
35
|
end
|
16
36
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
37
|
+
sub_class = Such.subclass(part, thing,
|
38
|
+
include: Such::Part, attr_accessor: plugs)
|
39
|
+
sub_class.singleton_class.class_eval{ define_method(:plugs){plugs} }
|
40
|
+
return sub_class
|
20
41
|
end
|
21
42
|
end
|
22
43
|
end
|
data/lib/such/such.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
module Such
|
2
|
-
def self.subclass(name,
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
def self.subclass(name, super_class, **including, &block)
|
3
|
+
# See documentation for Class#new
|
4
|
+
sub_class = const_set(name, Class.new(super_class))
|
5
|
+
# To be able to say like:
|
6
|
+
# include Such::Thing
|
7
|
+
# attr_accessor :attribute2, :attribute2
|
8
|
+
including.each{|these, mods| sub_class.public_send(these, *mods)}
|
9
|
+
# And to explicitly define methods in the subclass:
|
10
|
+
sub_class.class_eval(&block) if block
|
11
|
+
return sub_class
|
7
12
|
end
|
8
13
|
end
|
data/lib/such/thing.rb
CHANGED
@@ -5,7 +5,7 @@ module Such
|
|
5
5
|
|
6
6
|
# PARAMETERS' pretense of being a Hash constant :P
|
7
7
|
@@PARAMETERS = {}
|
8
|
-
PARAMETERS = lambda{
|
8
|
+
PARAMETERS = lambda{@@PARAMETERS[_1]}
|
9
9
|
def PARAMETERS.to_h = @@PARAMETERS
|
10
10
|
def self.configure(conf)
|
11
11
|
@@PARAMETERS = conf
|
@@ -61,7 +61,7 @@ module Such
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def self.which_method(container, methods=INTOS)
|
64
|
-
mthd = methods.detect{
|
64
|
+
mthd = methods.detect{container.respond_to?_1}
|
65
65
|
raise "Don't know how to put into #{container.class}." if mthd.nil?
|
66
66
|
return mthd
|
67
67
|
end
|
@@ -69,7 +69,7 @@ module Such
|
|
69
69
|
def self.into(obj, container=nil, mthd=nil, *args)
|
70
70
|
if container
|
71
71
|
if mthd
|
72
|
-
unless
|
72
|
+
unless container.respond_to?(mthd)
|
73
73
|
raise "Need container & method. Got #{container.class}##{mthd}(#{obj.class}...)"
|
74
74
|
end
|
75
75
|
else
|
@@ -90,7 +90,7 @@ module Such
|
|
90
90
|
rescue ArgumentError, TypeError
|
91
91
|
# Assume user meant to iterate. Note that the heuristic is not perfect.
|
92
92
|
$stderr.puts "# Iterated Method #{mthd}." if $VERBOSE
|
93
|
-
[*args].each{
|
93
|
+
[*args].each{m.call(*_1)}
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -105,9 +105,8 @@ module Such
|
|
105
105
|
|
106
106
|
def self.do_links(obj, signals, block)
|
107
107
|
return if signals.first==''
|
108
|
-
none = (signals.length==0)
|
109
108
|
if block
|
110
|
-
signals
|
109
|
+
signals=SIGNALS if signals.empty?
|
111
110
|
signals.each do |signal|
|
112
111
|
break if signal==''
|
113
112
|
begin
|
@@ -117,11 +116,15 @@ module Such
|
|
117
116
|
warn "Warning: no #{signal} signal for #{obj.class}"
|
118
117
|
end
|
119
118
|
end
|
120
|
-
elsif
|
119
|
+
elsif !signals.empty?
|
121
120
|
warn "Warning: No block given for #{signals.join(',')} on #{obj.class}."
|
122
121
|
end
|
123
122
|
end
|
124
123
|
|
124
|
+
# Given an Object not sub-classed as a Thing:
|
125
|
+
# obj = NotAThing.new
|
126
|
+
# One can still act on it like a Thing as follows:
|
127
|
+
# Thing.do_config(obj, *parameters, &block)
|
125
128
|
def self.do_config(obj, *parameters, &block)
|
126
129
|
container, arguments, methods, signals = Thing.do_parameters(parameters)
|
127
130
|
Thing.do_methods(obj, methods, container)
|
@@ -129,6 +132,7 @@ module Such
|
|
129
132
|
warn "Warning: arguments not used in do_config(#{obj.class}...)." if arguments.length > 0
|
130
133
|
end
|
131
134
|
|
135
|
+
# The alternate sub-class constructor:
|
132
136
|
def initialize(*parameters, &block)
|
133
137
|
container, arguments, methods, signals = Thing.do_parameters(parameters)
|
134
138
|
super(*arguments)
|
data/lib/such/things.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
module Such
|
2
2
|
module Things
|
3
|
-
def self.list(
|
4
|
-
ObjectSpace.each_object(Class).select{
|
3
|
+
def self.list(super_class)
|
4
|
+
ObjectSpace.each_object(Class).select{_1 < super_class}
|
5
5
|
end
|
6
6
|
|
7
|
-
def self.subclass(
|
8
|
-
Such.subclass(
|
7
|
+
def self.subclass(sub_class)
|
8
|
+
Such.subclass(sub_class.name.sub(/^.*::/,'').to_sym, sub_class,
|
9
|
+
include: Such::Thing)
|
9
10
|
end
|
10
11
|
|
11
|
-
def self.in(
|
12
|
-
Things.list(
|
12
|
+
def self.in(super_class)
|
13
|
+
Things.list(super_class).each do |sub_class|
|
13
14
|
begin
|
14
|
-
Things.subclass(
|
15
|
+
Things.subclass(sub_class)
|
15
16
|
rescue
|
16
|
-
$stderr.puts "#{$!.class}:\t#{
|
17
|
+
$stderr.puts "#{$!.class}:\t#{super_class}" if $VERBOSE
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
data/lib/such.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
module Such
|
2
|
-
VERSION = '2.
|
2
|
+
VERSION = '2.1.230106'
|
3
|
+
require 'such/such'
|
4
|
+
autoload :Convention, 'such/convention.rb'
|
5
|
+
autoload :Thing, 'such/thing.rb'
|
6
|
+
autoload :Things, 'such/things.rb'
|
7
|
+
autoload :Part, 'such/part.rb'
|
8
|
+
autoload :Parts, 'such/parts.rb'
|
3
9
|
end
|
4
|
-
require 'such/such'
|
5
|
-
require 'such/thing'
|
6
|
-
require 'such/things'
|
7
|
-
require 'such/part'
|
8
|
-
require 'such/parts'
|
9
10
|
# Requires:
|
10
11
|
#`ruby`
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: such
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.230106
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
7
|
+
- CarlosJHR64
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
14
|
Wraps widgets with an alternate constructor
|
@@ -22,6 +22,7 @@ extra_rdoc_files: []
|
|
22
22
|
files:
|
23
23
|
- README.md
|
24
24
|
- lib/such.rb
|
25
|
+
- lib/such/convention.rb
|
25
26
|
- lib/such/part.rb
|
26
27
|
- lib/such/parts.rb
|
27
28
|
- lib/such/such.rb
|
@@ -31,7 +32,7 @@ homepage: https://github.com/carlosjhr64/such
|
|
31
32
|
licenses:
|
32
33
|
- MIT
|
33
34
|
metadata: {}
|
34
|
-
post_install_message:
|
35
|
+
post_install_message:
|
35
36
|
rdoc_options: []
|
36
37
|
require_paths:
|
37
38
|
- lib
|
@@ -46,9 +47,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
47
|
- !ruby/object:Gem::Version
|
47
48
|
version: '0'
|
48
49
|
requirements:
|
49
|
-
- 'ruby: ruby 3.0
|
50
|
-
rubygems_version: 3.2
|
51
|
-
signing_key:
|
50
|
+
- 'ruby: ruby 3.2.0 (2022-12-25 revision a528908271) [aarch64-linux]'
|
51
|
+
rubygems_version: 3.4.2
|
52
|
+
signing_key:
|
52
53
|
specification_version: 4
|
53
54
|
summary: Wraps widgets with an alternate constructor which factors out the configuration
|
54
55
|
and assembly procedures into metadata. Can be used to wrap any class with the alternate
|