weft 0.1.0 → 0.2.0

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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +134 -28
  3. data/README.md +46 -23
  4. data/docs/app-patterns.md +8 -7
  5. data/docs/arbre.md +49 -18
  6. data/docs/configuration.md +42 -35
  7. data/docs/dsl.md +356 -105
  8. data/docs/error-handling.md +64 -24
  9. data/docs/examples/active-search.md +10 -10
  10. data/docs/examples/browser-dialogs.md +10 -10
  11. data/docs/examples/bulk-update.md +11 -11
  12. data/docs/examples/click-to-edit.md +14 -14
  13. data/docs/examples/click-to-load.md +8 -8
  14. data/docs/examples/delete-row.md +17 -19
  15. data/docs/examples/edit-row.md +17 -14
  16. data/docs/examples/file-upload.md +5 -5
  17. data/docs/examples/infinite-scroll.md +8 -8
  18. data/docs/examples/inline-expansion.md +8 -8
  19. data/docs/examples/inline-validation.md +17 -17
  20. data/docs/examples/lazy-loading.md +8 -8
  21. data/docs/examples/live-ticker.md +1 -1
  22. data/docs/examples/modal-dialog.md +3 -3
  23. data/docs/examples/progress-bar.md +1 -1
  24. data/docs/examples/reset-user-input.md +7 -7
  25. data/docs/examples/tabs.md +4 -4
  26. data/docs/examples/tooltip.md +8 -8
  27. data/docs/examples/updating-other-content.md +9 -9
  28. data/docs/examples/value-select.md +11 -11
  29. data/docs/params.md +112 -0
  30. data/docs/routing.md +13 -13
  31. data/docs/tutorial.md +46 -48
  32. data/lib/weft/action.rb +4 -2
  33. data/lib/weft/autoloading.rb +69 -0
  34. data/lib/weft/component.rb +97 -31
  35. data/lib/weft/configuration.rb +37 -5
  36. data/lib/weft/context/expansion.rb +184 -0
  37. data/lib/weft/context/interception.rb +22 -2
  38. data/lib/weft/context/modifiers.rb +78 -0
  39. data/lib/weft/context/traversal.rb +80 -0
  40. data/lib/weft/context/wiring.rb +85 -0
  41. data/lib/weft/context.rb +70 -164
  42. data/lib/weft/defaults/error_component.rb +57 -21
  43. data/lib/weft/defaults/error_page.rb +12 -10
  44. data/lib/weft/defaults/not_found_component.rb +14 -12
  45. data/lib/weft/defaults/not_found_page.rb +9 -8
  46. data/lib/weft/dsl/actions.rb +9 -9
  47. data/lib/weft/dsl/inclusions.rb +48 -11
  48. data/lib/weft/dsl/params.rb +265 -0
  49. data/lib/weft/dsl/recoveries.rb +36 -6
  50. data/lib/weft/dsl/sandbox.rb +26 -0
  51. data/lib/weft/dsl/triggers.rb +28 -8
  52. data/lib/weft/dsl/updates.rb +31 -8
  53. data/lib/weft/error.rb +12 -1
  54. data/lib/weft/page/assets.rb +222 -0
  55. data/lib/weft/page/head.rb +87 -0
  56. data/lib/weft/page.rb +55 -239
  57. data/lib/weft/params/assembly.rb +170 -0
  58. data/lib/weft/params.rb +138 -0
  59. data/lib/weft/presets.rb +96 -0
  60. data/lib/weft/redirect.rb +7 -7
  61. data/lib/weft/registry/eligibility.rb +5 -19
  62. data/lib/weft/registry.rb +58 -18
  63. data/lib/weft/resolver.rb +48 -20
  64. data/lib/weft/router/actions.rb +106 -22
  65. data/lib/weft/router/errors.rb +223 -83
  66. data/lib/weft/router/oob_includes.rb +202 -17
  67. data/lib/weft/router/streaming.rb +86 -20
  68. data/lib/weft/router.rb +33 -25
  69. data/lib/weft/version.rb +1 -1
  70. data/lib/weft.rb +37 -24
  71. metadata +32 -8
  72. data/lib/weft/attributes.rb +0 -65
  73. data/lib/weft/dsl/attributes.rb +0 -43
  74. data/lib/weft/shorthands.rb +0 -57
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: weft
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Rusterholz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-12 00:00:00.000000000 Z
11
+ date: 2026-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bigdecimal
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: sinatra
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -53,19 +67,19 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '3.0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: sinatra-contrib
70
+ name: zeitwerk
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
60
74
  - !ruby/object:Gem::Version
61
- version: '3.0'
75
+ version: '2.6'
62
76
  type: :runtime
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - ">="
67
81
  - !ruby/object:Gem::Version
68
- version: '3.0'
82
+ version: '2.6'
69
83
  description: Weft is a rapid web development framework where front-end components
70
84
  declare what behaviors are possible, and the back-end routing and request handling
71
85
  is derived automatically. No routes. No controllers. Just Weft.
@@ -105,29 +119,40 @@ files:
105
119
  - docs/examples/tooltip.md
106
120
  - docs/examples/updating-other-content.md
107
121
  - docs/examples/value-select.md
122
+ - docs/params.md
108
123
  - docs/routing.md
109
124
  - docs/tutorial.md
110
125
  - lib/weft.rb
111
126
  - lib/weft/action.rb
112
- - lib/weft/attributes.rb
127
+ - lib/weft/autoloading.rb
113
128
  - lib/weft/component.rb
114
129
  - lib/weft/configuration.rb
115
130
  - lib/weft/context.rb
131
+ - lib/weft/context/expansion.rb
116
132
  - lib/weft/context/interception.rb
133
+ - lib/weft/context/modifiers.rb
134
+ - lib/weft/context/traversal.rb
135
+ - lib/weft/context/wiring.rb
117
136
  - lib/weft/defaults.rb
118
137
  - lib/weft/defaults/error_component.rb
119
138
  - lib/weft/defaults/error_page.rb
120
139
  - lib/weft/defaults/not_found_component.rb
121
140
  - lib/weft/defaults/not_found_page.rb
122
141
  - lib/weft/dsl/actions.rb
123
- - lib/weft/dsl/attributes.rb
124
142
  - lib/weft/dsl/containers.rb
125
143
  - lib/weft/dsl/inclusions.rb
144
+ - lib/weft/dsl/params.rb
126
145
  - lib/weft/dsl/recoveries.rb
146
+ - lib/weft/dsl/sandbox.rb
127
147
  - lib/weft/dsl/triggers.rb
128
148
  - lib/weft/dsl/updates.rb
129
149
  - lib/weft/error.rb
130
150
  - lib/weft/page.rb
151
+ - lib/weft/page/assets.rb
152
+ - lib/weft/page/head.rb
153
+ - lib/weft/params.rb
154
+ - lib/weft/params/assembly.rb
155
+ - lib/weft/presets.rb
131
156
  - lib/weft/redirect.rb
132
157
  - lib/weft/registry.rb
133
158
  - lib/weft/registry/eligibility.rb
@@ -137,7 +162,6 @@ files:
137
162
  - lib/weft/router/errors.rb
138
163
  - lib/weft/router/oob_includes.rb
139
164
  - lib/weft/router/streaming.rb
140
- - lib/weft/shorthands.rb
141
165
  - lib/weft/version.rb
142
166
  homepage: https://github.com/rusterholz/weft
143
167
  licenses:
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Weft
4
- # Value object representing a component's resolved wire attributes.
5
- # Provides method-style access with a clear collision-resolution rule:
6
- # declared attribute names win, then the underlying Hash API is available
7
- # for any name not declared as an attribute.
8
- #
9
- # Action callables receive a ready-made instance (the sole argument to a
10
- # +performs+/+transfers+ block); you don't construct these yourself:
11
- #
12
- # attrs.status # => "shipped" (declared attribute)
13
- # attrs.count # => 42 (declared attribute — wins over Hash#count)
14
- # attrs[:status] # => "shipped" (explicit hash access)
15
- # attrs.select { ... } # delegates to the underlying hash
16
- # attrs.to_h # => the underlying hash (explicit escape hatch)
17
- class Attributes
18
- # Build an Attributes instance by extracting declared keys from a raw
19
- # attributes hash, applying defaults for missing keys. Does not mutate
20
- # the raw hash.
21
- #
22
- # schema = { status: { default: "pending" }, count: { default: 0 } }
23
- # raw = { status: "shipped", class: "big" }
24
- # Weft::Attributes.extract_from(raw, using: schema)
25
- # # => Attributes{ status: "shipped", count: 0 }
26
- def self.extract_from(raw, using:)
27
- data = using.to_h do |name, meta|
28
- [name, raw.fetch(name, meta[:default])]
29
- end
30
- new(data)
31
- end
32
-
33
- # @api private
34
- # Constructed internally (see {.extract_from} and the Router's resolver).
35
- def initialize(data)
36
- @data = data
37
- end
38
-
39
- def [](key)
40
- @data[key]
41
- end
42
-
43
- def key?(key)
44
- @data.key?(key)
45
- end
46
-
47
- def to_h
48
- @data
49
- end
50
-
51
- def respond_to_missing?(name, include_private = false)
52
- @data.key?(name) || @data.respond_to?(name, include_private) || super
53
- end
54
-
55
- def method_missing(name, *args, **kwargs, &block)
56
- if @data.key?(name) && args.empty? && kwargs.empty? && !block
57
- @data[name]
58
- elsif @data.respond_to?(name)
59
- @data.public_send(name, *args, **kwargs, &block)
60
- else
61
- super
62
- end
63
- end
64
- end
65
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Weft
4
- module DSL
5
- # Mixin for classes that declare wire-state attributes.
6
- # Provides the `attribute` class DSL and `attrs` instance reader.
7
- # Used by Component (for partial route params) and Page (for page route params).
8
- module Attributes
9
- def self.included(base)
10
- base.extend(ClassMethods)
11
- end
12
-
13
- module ClassMethods
14
- # Declare a wire attribute. Accepts optional default: and type: kwargs.
15
- # attribute :status, default: "active", type: :string
16
- def attribute(name, default: nil, **options)
17
- meta = { default: default }
18
- meta[:type] = options[:type] if options.key?(:type)
19
- own_attributes[name] = meta
20
- end
21
-
22
- # Returns all declared attributes (own + inherited), preserving declaration order.
23
- def attributes
24
- if superclass.respond_to?(:attributes)
25
- superclass.attributes.merge(own_attributes)
26
- else
27
- own_attributes.dup
28
- end
29
- end
30
-
31
- private
32
-
33
- def own_attributes
34
- @own_attributes ||= {}
35
- end
36
- end
37
-
38
- # Instance access to resolved wire attribute values.
39
- # Returns a Weft::Attributes object with method-style and hash access.
40
- attr_reader :attrs
41
- end
42
- end
43
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Weft
4
- # Registry for named interaction shorthands. Each shorthand is a preset of
5
- # trigger/swap/target defaults that Context expands via loads:.
6
- #
7
- # Shipped presets are registered at the bottom of this file. Users will be
8
- # able to register custom shorthands in v1.x via the same API.
9
- module Shorthands
10
- class << self
11
- # Register a named interaction shorthand.
12
- #
13
- # Weft::Shorthands.register :tooltip, trigger: :hover, swap: :fill
14
- def register(name, **defaults)
15
- registry[name] = defaults
16
- end
17
-
18
- # Look up a registered shorthand by name. Returns the defaults hash or nil.
19
- def lookup(name)
20
- registry[name]
21
- end
22
-
23
- # All registered shorthand names.
24
- def registered
25
- registry.keys
26
- end
27
-
28
- private
29
-
30
- def registry
31
- @registry ||= {}
32
- end
33
- end
34
- end
35
- end
36
-
37
- # Shipped interaction shorthands. Adding a new one is one line.
38
- # The mechanism (Context dispatch + loads: expansion) handles the rest.
39
- #
40
- # NOTE: "shorthand" naming is provisional — may be renamed to "pattern"
41
- # or similar after evaluation (Mission 29).
42
-
43
- Weft::Shorthands.register :tooltip, trigger: :hover, swap: :fill
44
- Weft::Shorthands.register :inline_expand, trigger: :click, swap: :after
45
- Weft::Shorthands.register :lazy, trigger: :visible, swap: :fill, target: :self
46
- Weft::Shorthands.register :modal, trigger: :click, swap: :fill
47
- Weft::Shorthands.register :load_more, trigger: :click, swap: :replace, target: :self
48
- Weft::Shorthands.register :infinite_scroll, trigger: :visible, swap: :after
49
- Weft::Shorthands.register :live_search, trigger: :input, swap: :fill
50
- Weft::Shorthands.register :tabs, trigger: :click, swap: :fill
51
-
52
- # Retry is the odd one out: its value is a URL (the failing component's own GET
53
- # URL, injected as the :retry_url recovery attr), not a target Class. It ships a
54
- # concrete hx-target so error components never hand-write htmx — outerHTML-swapping
55
- # the enclosing .weft-error box replaces the whole error display with the fresh
56
- # component. Callers override target: for a differently-classed container.
57
- Weft::Shorthands.register :retry, trigger: :click, swap: :outer_html, target: "closest .weft-error"