such 2.0.210201 → 2.1.230106

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84767a253e3e1535798068135516617e3c198fc4b3ed756e529c16c27f25a649
4
- data.tar.gz: 919ffcc84f13208bb3fc232ad8dc2ac92eb16b40d0cc6f63bb81b2b79c8b7e63
3
+ metadata.gz: c296ea57df2d19bf9db70eb35f97494e15b8c739d5b0e8cea5a31163c457dc76
4
+ data.tar.gz: a1c10cf419d67311a9b9e33fdf26a3f587052af4a5a3260a6b3f2db90b255056
5
5
  SHA512:
6
- metadata.gz: d53f03a3dc85ad766199e40d0f7d08011420ec1c437de865cdda76f2757a8c4bac012379098fd087af1f78ce11918be949b46fe0cff68668806df237a175a5f0
7
- data.tar.gz: 96c391adf17465cdf4330a37c83eece66a1d4ae37337067fc8b04cd1f0105fe5f4b45b4863371c60d558552980e5ab744f9cb817c4c26322895b50139567760a
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.0.210201](https://github.com/carlosjhr64/such/releases)
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
- $ sudo gem install such
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 constructer's super,
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 contructor will be as if the following was run:
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 behaviour
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 the powerful
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) 2021 CarlosJHR64
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, sym, cls = "#{plg}=".to_sym, "#{md[:sym]}!".to_sym, Object.const_get("Such::#{md[:cls]}")
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
- def method_missing(maybe,*args) # maybe a plug down the plugged things.
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, Class thing < Such::Thing, *Symbol plugs)"
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_]+_(?<klass>[^\W_]+)$/=~plug
28
+ if /^[^\W_]+_(?<super_class>[^\W_]+)$/=~plug
9
29
  next unless $VERBOSE
10
- unless Object.const_defined?("Such::#{klass}")
11
- $stderr.puts "Warning: Such::#{klass} not defined yet."
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
- subklass = Such.subclass(part, thing, include: Such::Part, attr_accessor: plugs)
18
- subklass.singleton_class.class_eval{ define_method(:plugs){plugs} }
19
- return subklass
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, klass, **kw, &block)
3
- subklass = const_set(name, Class.new(klass))
4
- kw.each{|method, args| subklass.public_send(method, *args)}
5
- subklass.class_eval(&block) if block
6
- return subklass
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{|k| @@PARAMETERS[k]}
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{|m| container.respond_to?(m)}
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 mthd.class==Symbol and container.respond_to?(mthd)
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{|arg| m.call(*arg)}
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.push(*SIGNALS) if none
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 not none
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(superklass)
4
- ObjectSpace.each_object(Class).select{|klass| klass < superklass}
3
+ def self.list(super_class)
4
+ ObjectSpace.each_object(Class).select{_1 < super_class}
5
5
  end
6
6
 
7
- def self.subclass(klass)
8
- Such.subclass(klass.name.sub(/^.*::/,'').to_sym, klass, include: Such::Thing)
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(superklass)
12
- Things.list(superklass).each do |klass|
12
+ def self.in(super_class)
13
+ Things.list(super_class).each do |sub_class|
13
14
  begin
14
- Things.subclass(klass)
15
+ Things.subclass(sub_class)
15
16
  rescue
16
- $stderr.puts "#{$!.class}:\t#{superklass}" if $VERBOSE
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.0.210201'
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.0.210201
4
+ version: 2.1.230106
5
5
  platform: ruby
6
6
  authors:
7
- - carlosjhr64
8
- autorequire:
7
+ - CarlosJHR64
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-01 00:00:00.000000000 Z
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.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]'
50
- rubygems_version: 3.2.3
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