signalize 1.2.0 → 1.3.0

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: d07f1ae6a575b06c9831c5a167f69fcc1432a653a907e1cc4ed392271b297e6c
4
- data.tar.gz: 053fdbe6ce667fee00b58f3ee910b58a7639b44093508dbdf4cc1bcccf09c488
3
+ metadata.gz: a0caca6f4fbd9e03da42b9a5d6774f41cd3a75e0c3531e6a4a856a6db46fda31
4
+ data.tar.gz: 405900c040ca95a168ab288cdd44d3bbfb563c3513ba34bf17ca4054262591ba
5
5
  SHA512:
6
- metadata.gz: 9f2e7666b585b08d1b9e770cd09e558f44fda4bd76be3a58b3355770116ac7e730563b1fba29d273151797ad5efc4aeee8705d195a9d4a21ece91fc247f8f846
7
- data.tar.gz: 0d44bbd6f232bb3c9e8f02d99f4d785826cb6ee885bb6be1c43503657dad1c7bbadbc55dc87a24d58d601e534e61497088d6a817b9aaee47808a28463dd4c6bb
6
+ metadata.gz: 2620410cdb72ea66cf1a6a9deef3d2d945b9208fd80c3d351a4946f31ae3288302c095420a3c8c4351688bef6fb788c936de5e2aead20e6003ae001d5303290c
7
+ data.tar.gz: 44c96f16d953c00127df8a21905515e29accdac583117ccd52d7c5f410fe190bc98962c173968bda2f460227745a462fade990aba26a4c4ddbbf55b6f23904b1
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.3.0] - 2023-10-04
6
+
7
+ - Provide `Signalize::Struct`, a struct-like object to hold multiple signals (including computed)
8
+ (optional via `require "signalize/struct"`)
9
+
5
10
  ## [1.2.0] - 2023-10-03
6
11
 
7
12
  - Add `untracked` method (implements #5)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- signalize (1.2.0)
4
+ signalize (1.3.0)
5
5
  concurrent-ruby (~> 1.2)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -25,7 +25,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
25
25
 
26
26
  ## Usage
27
27
 
28
- Signalize's public API consists of four methods (you can think of them almost like functions): `signal`, `computed`, `effect`, and `batch`.
28
+ Signalize's public API consists of five methods (you can think of them almost like functions): `signal`, `untracked`, `computed`, `effect`, and `batch`.
29
29
 
30
30
  ### `signal(initial_value)`
31
31
 
@@ -232,7 +232,7 @@ end
232
232
  counter.value = 1 # logs the new value
233
233
  ```
234
234
 
235
- ### `peek`
235
+ ### `signal.peek`
236
236
 
237
237
  If you need to access a signal's value inside an effect without subscribing to that signal's updates, use the `peek` method instead of `value`.
238
238
 
@@ -252,6 +252,55 @@ effect do
252
252
  end
253
253
  ```
254
254
 
255
+ ## Signalize Struct
256
+
257
+ An optional add-on to Signalize, the `Singalize::Struct` class lets you define multiple signal or computed variables to hold in struct-like objects. You can even add custom methods to your classes with a simple DSL. (The API is intentionally similar to `Data` in Ruby 3.2+, although these objects are of course mutable.)
258
+
259
+ Here's what it looks like:
260
+
261
+ ```ruby
262
+ require "signalize/struct"
263
+
264
+ include Signalize::API
265
+
266
+ TestSignalsStruct = Signalize::Struct.define(
267
+ :str,
268
+ :int,
269
+ :multiplied_by_10
270
+ ) do # optional block for adding methods
271
+ def increment!
272
+ self.int += 1
273
+ end
274
+ end
275
+
276
+ struct = TestSignalsStruct.new(
277
+ int: 0,
278
+ str: "Hello World",
279
+ multiplied_by_10: computed { struct.int * 10 }
280
+ )
281
+
282
+ effect do
283
+ puts struct.multiplied_by_10 # 0
284
+ end
285
+
286
+ effect do
287
+ puts struct.str # "Hello World"
288
+ end
289
+
290
+ struct.increment! # above effect will now output 10
291
+ struct.str = "Goodbye!" # above effect will now output "Goodbye!"
292
+ ```
293
+
294
+ If you ever need to get at the actual `Signal` object underlying a value, just call `*_signal`. For example, you could call `int_signal` for the above example to get a signal object for `int`.
295
+
296
+ Signalize structs require all of their members to be present when initializing…you can't pass only some keyword arguments.
297
+
298
+ Signalize structs support `to_h` as well as `deconstruct_keys` which is used for pattern matching and syntax like `struct => { str: }` to set local variables.
299
+
300
+ You can call `members` (as both object/class methods) to get a list of the value names in the struct.
301
+
302
+ Finally, both `inspect` and `to_s` let you debug the contents of a struct.
303
+
255
304
  ## Development
256
305
 
257
306
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rake test` to run the tests, or `bin/guard` or run them continuously in watch mode. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -260,7 +309,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
260
309
 
261
310
  ## Contributing
262
311
 
263
- Signalize is considered a direct port of the [original Signals JavaScript library](https://github.com/preactjs/signals). This means we are unlikely to accept any additional features other than what's provided by Signals. If Signals adds new functionality in the future, we will endeavor to replicate it in Signalize. Furthermore, if there's some unwanted behavior in Signalize that's also present in Signals, we are unlikely to modify that behavior.
312
+ Signalize is considered a direct port of the [original Signals JavaScript library](https://github.com/preactjs/signals). This means we are unlikely to accept any additional features other than what's provided by Signals (unless it's completely separate, like our `Signalize::Struct` add-on). If Signals adds new functionality in the future, we will endeavor to replicate it in Signalize. Furthermore, if there's some unwanted behavior in Signalize that's also present in Signals, we are unlikely to modify that behavior.
264
313
 
265
314
  However, if you're able to supply a bugfix or performance optimization which will help bring Signalize _more_ into alignment with its Signals counterpart, we will gladly accept your PR!
266
315
 
@@ -0,0 +1,91 @@
1
+ require "signalize"
2
+
3
+ module Signalize
4
+ class Struct
5
+ module Accessors
6
+ def members
7
+ @members ||= []
8
+ end
9
+
10
+ def signal_accessor(*names)
11
+ names.each do |name|
12
+ members.push(name.to_sym) unless members.find { _1 == name.to_sym }
13
+ signal_getter_name = "#{name}_signal".freeze
14
+ ivar_name = "@#{name}".freeze
15
+
16
+ define_method "#{name}_signal" do
17
+ instance_variable_get(ivar_name)
18
+ end
19
+
20
+ define_method name do
21
+ send(signal_getter_name)&.value
22
+ end
23
+
24
+ define_method "#{name}=" do |val|
25
+ if instance_variable_defined?(ivar_name)
26
+ raise Signalize::Error, "Cannot assign a signal to a signal value" if val.is_a?(Signalize::Signal)
27
+
28
+ sig = instance_variable_get(ivar_name)
29
+ if sig.is_a?(Signalize::Computed)
30
+ raise Signalize::Error, "Cannot set value of computed signal `#{ivar_name.delete_prefix("@")}'"
31
+ end
32
+
33
+ sig.value = val
34
+ else
35
+ val = Signalize.signal(val) unless val.is_a?(Signalize::Computed)
36
+ instance_variable_set(ivar_name, val)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ extend Accessors
44
+
45
+ def self.define(*names, &block)
46
+ Class.new(self).tap do |struct|
47
+ struct.signal_accessor(*names)
48
+ struct.class_eval(&block) if block
49
+ end
50
+ end
51
+
52
+ def initialize(**data)
53
+ # The below code is all to replicate native Ruby ergonomics
54
+ unknown_keys = data.keys - members
55
+ unless unknown_keys.empty?
56
+ plural_suffix = unknown_keys.length > 1 ? "s" : ""
57
+ raise ArgumentError, "unknown keyword#{plural_suffix}: #{unknown_keys.map { ":#{_1}" }.join(", ")}"
58
+ end
59
+
60
+ missing_keys = members - data.keys
61
+ unless missing_keys.empty?
62
+ plural_suffix = missing_keys.length > 1 ? "s" : ""
63
+ raise ArgumentError, "missing keyword#{plural_suffix}: #{missing_keys.map { ":#{_1}" }.join(", ")}"
64
+ end
65
+
66
+ # Initialize with keyword arguments
67
+ data.each do |k, v|
68
+ send("#{k}=", v)
69
+ end
70
+ end
71
+
72
+ def members = self.class.members
73
+
74
+ def deconstruct_keys(...) = to_h.deconstruct_keys(...)
75
+
76
+ def to_h = members.each_with_object({}) { _2[_1] = send("#{_1}_signal").peek }
77
+
78
+ def inspect
79
+ var_peeks = instance_variables.filter_map do |var_name|
80
+ var = instance_variable_get(var_name)
81
+ "#{var_name.to_s.delete_prefix("@")}=#{var.peek.inspect}" if var.is_a?(Signalize::Signal)
82
+ end.join(", ")
83
+
84
+ "#<#{self.class}#{var_peeks.empty? ? nil : " #{var_peeks}"}>"
85
+ end
86
+
87
+ def to_s
88
+ inspect
89
+ end
90
+ end
91
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Signalize
4
- VERSION = "1.2.0"
4
+ VERSION = "1.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: signalize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jared White
@@ -42,6 +42,7 @@ files:
42
42
  - README.md
43
43
  - Rakefile
44
44
  - lib/signalize.rb
45
+ - lib/signalize/struct.rb
45
46
  - lib/signalize/version.rb
46
47
  - signalize.gemspec
47
48
  homepage: https://github.com/whitefusionhq/signalize